c语言win api编程,RichEdit控件基础知识之二-window API 编程 c语言实现

今天的这个课程,我们继续为各位朋友们讲解关于RichEdit控件基础知识,大家需要好好的学习和掌握这个控件方面的知识哦。因为它还是非常的重要的哦。所以我们来好好进行对这个控件讲解和分析哦。

设置正文/保存正文

对于那些经常使用 Edit 控件的人来说,你们肯定对使用 WM_GETTEXT / WM_SETTEXT 来设置 / 保存

控制中的正文的方法很熟悉。这个方法对 RichEdit 仍然适用,但是如果文件很大的话这个方法不再是最有效的。 Edit

控件限制了正文长度,可以输入最多 64K 的正文。但是 RichEdit

控件可以接受比这个限制大的多的正文数据。要分配一个很大的内存块 ( 譬如大约 10MB ) 来接受来自来自 WM_GETTEXT

消息的正文是一件很麻烦的事 . Richedit 控件为此提供了一个新的方法,就是 正文流( Text Streaming

)。

要设置 RichEdit 文本,你只需给 RichEdit 简单的提供一个回调函数的地址,当一切准备好时, RichEdit

会调用回调函数,并将正文缓冲区的地址传递给它。回调函数会将要发送给 RichEdit

的数据填入缓冲区或者将缓冲区的数据读出,然后等待下一次调用自到操作完成。

范例程序是流入(设置正文)和流出(取出正文)两者的例子。你将会发现这个方法更加有效:这个缓冲区是 RichEdit

控件自己提供的,因此数据被分成了几个大块。这个操作包括两条消息 : EM_STREAMIN 和 EM_STREAMOUT

EM_STREAMIN 和 EM_STREAMOUT 两者使用同一个语法 :

wParam == 格式选项 .

SF_RTF

数据是 RTF 格式。

SF_TEXT

数据是简单正文格式。

SFF_PLAINRTF

只有那些对所有语系都共有的关键词才流入。

SFF_SELECTION

如果指定这个标志,流操作的目标就是当前选定的正文。如果你将正文流入,当前正文就会被替换,如果是流出,则只有那些当前选定的正文才流出。如果没有指定这个标志,操作就会影响到控件中的所有正文。

SF_UNICODE

(RichEdit 2.0 或更高版本才提供)指定的是 Unicode 正文。

lParam == 指向一个 EDITSTREAM 结构,该结构定义如下:

typedef struct _editstream {

DWORD_PTR dwCookie;

DWORD dwError;

EDITSTREAMCALLBACK pfnCallback;

} EDITSTREAM;

dwCookie

应用程序定义的数值,将会被传递给由 pfnCallback 成员说明的回调函数。通常地我们传递一些重要的参数值给回调函数,譬如流入 /

流出 处理中使用到的文件句柄。

dwError

指示流入(读)或流出(写)操作的结果。 0 说明没有错误。非 0 值可以是 EditStreamCallback

函数的返回值或者说明控件遇到了错误。

pfnCallback

指向 EditStreamCallback 函数的指针,该函数是由用户定义,由 RichEdit 调用来传输数据的。 RichEdit

将数据分开多个部分,重复地调用该函数,一次一部分地进行数据传输。

EditStream 回调函数具有以下定义:

DWORD EditStreamCallback(

DWORD_PTR dwCookie ,

LPBYTE pbBuff ,

LONG cb ,

LONG * pcb

);

你必须在程序中按照以上原型来创建回调函数。然后将函数地址通过 EDITSTREAM 结构传递给 EM_STREAMIN 或者

EM_STREAMOUT 。

对流入操作 ( 设置 RichEdit 控件中的正文 ):

dwCookie : 应用程序定义的数值,通过 EDITSTREAM 结构传递给 EM_STREAMIN 。

在这里我们几乎

全部都是将用其内容来设置 RichEdit 正文的文件的句柄传给这个参数。

pbBuff : 指向 RichEdit 提供的缓冲区。用来接受回调函数中提供的正文数据。

cb : 本次调用中你可以写入缓冲区 ( pbBuff ) 的最大正文字节数。你 必须

遵守这个限制,

也就是说你发送的数据的大小可以比cb指定要小,但是不能大于这个值。你可以将这个数值当作

pbBuff 缓冲区的大小。

pcb : 指向一个双字 (DWORD)

,你必须设置这个值来指示你实际上传输了多少字节数据到缓冲区。

这个值通常跟 cb 中的值是相等的 .

唯一的例外是当要传送的数据少于缓冲区提供的大小时,

譬如到达文件尾时就是了。

对流出操作 ( 读出 RichEdit 控件的正文 ):

dwCookie : 跟流入操作相同 . 我们一般传递想要将数据写入的文件的句柄给这个参数。

pbBuff : 指向由 RichEdit 提供的缓冲区,里面的是 RichEdit 的正文。要得到其大小,你必须检查 cb 的

值.

cb : pbBuff 指向的缓冲区的的数据的大小。

pcb : 指向一个双字 (DWORD)

,你必须设置这个值来指示你实际上从缓冲区里读出了多少字节数据。

回调函数返回 0 说明操作成功,而且如果还有数据需要读 / 写的话, RichEdit 控件会继续调用它。如

果操作中发生了错误,而且你想停止操作的话,你可以返回一个非 0 值,这样 RichEdit 就会丢弃pbBuff 指向的数据。错误 /

成功返回值会在 EDITSTREAM 的 dwError 成员中返回,你可以 在 SendMessage 返回后检查流操作的错误 /

成功状态。

下面的例子是一个简单的编辑器,你可以用来打开一个cpp源文件,编辑它,然后保存。它使用了 RichEdit 控件 2.0

或者更,讲解了一些理论方面的知识,下面我们来看看一个具体的代码例子程序哦。

#include"Windows.h"

#include"tchar.h"

#include"Richedit.h"

#defineIDR_MAINMENU101

#defineIDM_OPEN40001

#defineIDM_SAVE40002

#defineIDM_CLOSE40003

#defineIDM_SAVEAS40004

#defineIDM_EXIT40005

#defineIDM_COPY40006

#defineIDM_CUT40007

#defineIDM_PASTE40008

#defineIDM_DELETE40009

#defineIDM_SELECTALL40010

#defineIDM_OPTION40011

#defineIDM_UNDO40012

#defineIDM_REDO40013

#defineIDD_OPTIONDLG101

#defineIDC_BACKCOLORBOX1000

#defineIDC_TEXTCOLORBOX1001

#defineRichEditID300

TCHARClassName[]=_T("IczEditClass");

TCHARAppName[]=_T("IczEdit version 1.0");

TCHARRichEditDLL[]=_T("riched20.dll");

TCHARRichEditClass[]=_T("RichEdit20A");

TCHARNoRichEdit[]=_T("Cannot find riched20.dll");

TCHARCppFilterString[]=_T("Cpp source code(*.cpp)\0*.cpp\0All Files (*.*)\0*.*\0");

TCHAROpenFileFail[]=_T("Cannot open the file");

TCHARWannaSave[]=_T("The data in the control is modified. Want to save it?");

BOOLFileOpened=FALSE;

COLORREFBackgroundColor=RGB(255,255,255);

COLORREFTextColor=RGB(0,0,0);

TCHARFileName[256];

TCHARAlternateFileName[256];

DWORDCustomColors[16];

HINSTANCE g_hInstance;

HMODULE hRichEdit;

HWND hwndRichEdit;

HMENU hMenu;

DWORD CALLBACKStreamInProc(DWORD hFile,LPBYTE pBuffer,longNumBytes,long*pBytesRead)

{

return(ReadFile((HANDLE)hFile,pBuffer,NumBytes,(LPDWORD)pBytesRead,0)^1);

}

DWORD CALLBACKStreamOutProc(DWORD hFile,LPBYTE pBuffer,longNumBytes,long*pBytesRead)

{

return(WriteFile((HANDLE)hFile,pBuffer,NumBytes,(LPDWORD)pBytesRead,0)^1);

}

BOOLCheckModifyState(HWND hWnd)

{

if(SendMessage(hwndRichEdit,EM_GETMODIFY,0,0))

{

intnResult=MessageBox(hWnd,WannaSave,AppName,MB_YESNOCANCEL);

if(nResult==IDYES)

{

SendMessage(hWnd,WM_COMMAND,IDM_SAVE,0);

}

elseif(nResult==IDCANCEL)

returnFALSE;

}

returnTRUE;

}

voidSetColor()

{

CHARFORMAT cfm;

SendMessage(hwndRichEdit,EM_SETBKGNDCOLOR,0,BackgroundColor);

RtlZeroMemory(&cfm,sizeof(cfm));

cfm.cbSize=sizeof(cfm);

cfm.dwMask=CFM_COLOR;

cfm.crTextColor=TextColor;

SendMessage(hwndRichEdit,EM_SETCHARFORMAT,SCF_ALL,(LPARAM)&cfm);

}

INT CALLBACKOptionProc(HWND hWnd,

UINTMsg,

WPARAM wParam,

LPARAM lParam

)

{

CHOOSECOLOR clr;

switch(Msg)

{

caseWM_INITDIALOG:

break;

caseWM_COMMAND:

if(HIWORD(wParam)==BN_CLICKED)

{

switch(LOWORD(wParam))

{

caseIDCANCEL:

SendMessage(hWnd,WM_CLOSE,0,0);

break;

caseIDC_BACKCOLORBOX:

RtlZeroMemory(&clr,sizeof(clr));

clr.lStructSize=sizeof(clr);

clr.hwndOwner=hWnd;

clr.hInstance=(HWND)g_hInstance;

clr.rgbResult=BackgroundColor;

clr.lpCustColors=CustomColors;

clr.Flags=CC_ANYCOLOR|CC_RGBINIT;

if(ChooseColor(&clr))

{

BackgroundColor=clr.rgbResult;

InvalidateRect(GetDlgItem(hWnd,IDC_BACKCOLORBOX),0,TRUE);

}

break;

caseIDC_TEXTCOLORBOX:

RtlZeroMemory(&clr,sizeof(clr));

clr.lStructSize=sizeof(clr);

clr.hwndOwner=hWnd;

clr.hInstance=(HWND)g_hInstance;

clr.rgbResult=TextColor;

clr.lpCustColors=CustomColors;

clr.Flags=CC_ANYCOLOR|CC_RGBINIT;

if(ChooseColor(&clr))

{

TextColor=clr.rgbResult;

InvalidateRect(GetDlgItem(hWnd,IDC_TEXTCOLORBOX),0,TRUE);

}

break;

caseIDOK:

{

DWORD dwRet=SendMessage(hwndRichEdit,EM_GETMODIFY,0,0);

SetColor();

SendMessage(hwndRichEdit,EM_SETMODIFY,dwRet,0);

EndDialog(hWnd,0);

}

break;

}

}

break;

caseWM_CTLCOLORSTATIC:

if(GetDlgItem(hWnd,IDC_BACKCOLORBOX)==(HWND)lParam)

{

return(int)CreateSolidBrush(BackgroundColor);

}

else

{

if(GetDlgItem(hWnd,IDC_TEXTCOLORBOX)==(HWND)lParam)

return(int)CreateSolidBrush(TextColor);

}

break;

caseWM_CLOSE:

EndDialog(hWnd,0);

break;

default:

returnFALSE;

}

returnTRUE;

}

LONG CALLBACKProcWinMain(HWND hWnd,

UINTMsg,

WPARAM wParam,

LPARAM lParam

)

{

CHARRANGE chrg;

OPENFILENAME ofn;

EDITSTREAM editstream;

HANDLE hFile;

switch(Msg)

{

caseWM_CREATE:

{

hwndRichEdit=CreateWindowEx(WS_EX_CLIENTEDGE,RichEditClass,NULL,ES_MULTILINE|WS_CHILD|WS_VISIBLE|WS_VSCROLL|WS_HSCROLL|ES_NOHIDESEL,

CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,hWnd,(HMENU)RichEditID,g_hInstance,NULL);

SendMessage(hwndRichEdit,EM_LIMITTEXT,-1,0);

SetColor();

SendMessage(hwndRichEdit,EM_SETMODIFY,FALSE,0);

SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER,0,0);

}

break;

caseWM_INITMENUPOPUP:

if(LOWORD(lParam)==0)

{

if(FileOpened)

{

EnableMenuItem((HMENU)wParam,IDM_OPEN,MF_GRAYED);

EnableMenuItem((HMENU)wParam,IDM_CLOSE,MF_ENABLED);

EnableMenuItem((HMENU)wParam,IDM_SAVE,MF_ENABLED);

EnableMenuItem((HMENU)wParam,IDM_SAVEAS,MF_ENABLED);

}

else

{

EnableMenuItem((HMENU)wParam,IDM_OPEN,MF_ENABLED);

EnableMenuItem((HMENU)wParam,IDM_CLOSE,MF_GRAYED);

EnableMenuItem((HMENU)wParam,IDM_SAVE,MF_GRAYED);

EnableMenuItem((HMENU)wParam,IDM_SAVEAS,MF_GRAYED);

}

}

elseif(LOWORD(lParam)==1)

{

if(SendMessage(hwndRichEdit,EM_CANPASTE,CF_TEXT,0))

EnableMenuItem((HMENU)wParam,IDM_PASTE,MF_ENABLED);

else

EnableMenuItem((HMENU)wParam,IDM_PASTE,MF_GRAYED);

if(SendMessage(hwndRichEdit,EM_CANREDO,CF_TEXT,0))

EnableMenuItem((HMENU)wParam,IDM_REDO,MF_ENABLED);

else

EnableMenuItem((HMENU)wParam,IDM_REDO,MF_GRAYED);

SendMessage(hwndRichEdit,EM_EXGETSEL,0,(LPARAM)&chrg);

if(chrg.cpMin==chrg.cpMax)

{

EnableMenuItem((HMENU)wParam,IDM_COPY,MF_GRAYED);

EnableMenuItem((HMENU)wParam,IDM_CUT,MF_GRAYED);

EnableMenuItem((HMENU)wParam,IDM_DELETE,MF_GRAYED);

}

else

{

EnableMenuItem((HMENU)wParam,IDM_COPY,MF_ENABLED);

EnableMenuItem((HMENU)wParam,IDM_CUT,MF_ENABLED);

EnableMenuItem((HMENU)wParam,IDM_DELETE,MF_ENABLED);

}

}

break;

caseWM_CLOSE:

if(CheckModifyState(hWnd))

DestroyWindow(hWnd);

break;

caseWM_DESTROY:

PostQuitMessage(0);

break;

caseWM_SIZE:

MoveWindow(hwndRichEdit,0,0,LOWORD(lParam),HIWORD(lParam),TRUE);

break;

caseWM_COMMAND:

if(lParam==0)

{

switch(LOWORD(wParam))

{

caseIDM_OPEN:

RtlZeroMemory(&ofn,sizeof(ofn));

ofn.lStructSize=sizeof(ofn);

ofn.hwndOwner=hWnd;

ofn.hInstance=g_hInstance;

ofn.lpstrFilter=CppFilterString;

ofn.lpstrFile=FileName;

ofn.nMaxFile=sizeof(FileName);

ofn.Flags=OFN_FILEMUSTEXIST|OFN_HIDEREADONLY|OFN_PATHMUSTEXIST;

if(GetOpenFileName(&ofn))

{

hFile=CreateFile(FileName,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);

if(hFile!=INVALID_HANDLE_VALUE)

{

editstream.dwCookie=(DWORD)hFile;

editstream.pfnCallback=StreamInProc;

SendMessage(hwndRichEdit,EM_STREAMIN,SF_TEXT,(LPARAM)&editstream);

SendMessage(hwndRichEdit,EM_SETMODIFY,FALSE,0);

CloseHandle(hFile);

FileOpened=TRUE;

}

else

MessageBox(hWnd,OpenFileFail,AppName,MB_OK|MB_ICONERROR);

}

break;

caseIDM_CLOSE:

if(CheckModifyState(hWnd))

{

SetWindowText(hwndRichEdit,0);

FileOpened=FALSE;

}

break;

caseIDM_SAVE:

{

hFile=CreateFile(FileName,GENERIC_WRITE,FILE_SHARE_READ,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,0);

if(hFile!=INVALID_HANDLE_VALUE)

{

editstream.dwCookie=(DWORD)hFile;

editstream.pfnCallback=StreamOutProc;

SendMessage(hwndRichEdit,EM_SETMODIFY,FALSE,0);

CloseHandle(hFile);

}

else

MessageBox(hWnd,OpenFileFail,AppName,MB_OK|MB_ICONERROR);

}

break;

caseIDM_COPY:

SendMessage(hwndRichEdit,WM_COPY,0,0);

break;

caseIDM_CUT:

SendMessage(hwndRichEdit,WM_CUT,0,0);

break;

caseIDM_PASTE:

SendMessage(hwndRichEdit,WM_PASTE,0,0);

break;

caseIDM_DELETE:

SendMessage(hwndRichEdit,EM_REPLACESEL,TRUE,0);

break;

caseIDM_SELECTALL:

chrg.cpMax=-1;

chrg.cpMin=0;

SendMessage(hwndRichEdit,EM_EXSETSEL,0,(LPARAM)&chrg);

break;

caseIDM_UNDO:

SendMessage(hwndRichEdit,EM_UNDO,0,0);

break;

caseIDM_REDO:

SendMessage(hwndRichEdit,EM_REDO,0,0);

break;

caseIDM_OPTION:

DialogBoxParam(g_hInstance,MAKEINTRESOURCE(IDD_OPTIONDLG),hWnd,&OptionProc,0);

break;

caseIDM_SAVEAS:

RtlZeroMemory(&ofn,sizeof(ofn));

ofn.lStructSize=sizeof(ofn);

ofn.hwndOwner=hWnd;

ofn.hInstance=g_hInstance;

ofn.lpstrFilter=CppFilterString;

ofn.lpstrFile=AlternateFileName;

ofn.nMaxFile=sizeof(AlternateFileName);

ofn.Flags=OFN_FILEMUSTEXIST|OFN_HIDEREADONLY|OFN_PATHMUSTEXIST;

if(GetSaveFileName(&ofn))

{

hFile=CreateFile(AlternateFileName,GENERIC_WRITE,FILE_SHARE_READ,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,0);

if(hFile!=INVALID_HANDLE_VALUE)

{

editstream.dwCookie=(DWORD)hFile;

editstream.pfnCallback=StreamOutProc;

SendMessage(hwndRichEdit,EM_SETMODIFY,FALSE,0);

CloseHandle(hFile);

}

else

MessageBox(hWnd,OpenFileFail,AppName,MB_OK|MB_ICONERROR);

}

break;

caseIDM_EXIT:

SendMessage(hWnd,WM_CLOSE,0,0);

break;

}

}

break;

default:

returnDefWindowProc(hWnd,Msg,wParam,lParam);

}

return0;

}

intWINAPIWinMain(HINSTANCE hInstance,

HINSTANCE hPrevInstance,

LPSTR lpCmdLine,

intnCmdShow

)

{

WNDCLASSEX wc;

MSG msg;

HWND hWnd;

g_hInstance=hInstance;

hRichEdit=LoadLibrary(RichEditDLL);

if(!hRichEdit)

{

MessageBox(0,NoRichEdit,AppName,MB_OK|MB_ICONERROR);

return0;

}

wc.cbSize=sizeof(WNDCLASSEX);

wc.style=NULL;

wc.lpfnWndProc=ProcWinMain;

wc.cbClsExtra=NULL;

wc.cbWndExtra=NULL;

wc.hInstance=hInstance;

wc.hbrBackground=(HBRUSH)(COLOR_WINDOW+1);

wc.lpszMenuName=MAKEINTRESOURCE(IDR_MAINMENU);

wc.lpszClassName=ClassName;

wc.hIcon=wc.hIconSm=LoadIcon(NULL,IDI_APPLICATION);

wc.hCursor=LoadCursor(NULL,IDC_ARROW);

RegisterClassEx(&wc);

hWnd=CreateWindowEx(NULL,ClassName,AppName,WS_OVERLAPPEDWINDOW,

CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,hInstance,NULL);

ShowWindow(hWnd,SW_SHOWNORMAL);

UpdateWindow(hWnd);

while(GetMessage(&msg,NULL,0,0))

{

TranslateMessage(&msg);

DispatchMessage(&msg);

}

FreeLibrary(hRichEdit);

returnmsg.wParam;

}

例子程序首先载入RichEdit DLL, 在这里是 riched20.dll. 如果DLL载入失败,就返回

Windows.

hRichEdit = LoadLibrary(RichEditDLL);

if(!hRichEdit)

{

MessageBox(0,NoRichEdit,AppName,MB_OK|MB_ICONERROR);

return 0;

}

成功载入DLL后,我们继续创建一个常规窗口,作为RichEdit的父窗口。在 WM_CREATE

处理函数里,我们创建一个RichEdit控件:

hwndRichEdit =

CreateWindowEx(WS_EX_CLIENTEDGE,RichEditClass,NULL,ES_MULTILINE |

WS_CHILD |WS_VISIBLE |WS_VSCROLL |WS_HSCROLL

|ES_NOHIDESEL,

CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,hWnd,(HMENU)RichEditID,g_hInstance,NULL);

注意在这里我们指定了 ES_MULTILINE

风格,否则创建的会是一个单行的控件。

SendMessage(hwndRichEdit,EM_LIMITTEXT,-1,0);

创建了RichEdit控件之后,我们必须设置新的正文大小。缺省时,RichEdit控件具有64K的正文大小限制,跟简单的多行Edit控件相同。我们需要扩展这个限制,允许用来操作更大的文件。在上一个代码行里,我们指定了大小为

-1, 大小总计为 0FFFFFFFFh 字节, 是一个很大的数值了。

SetColor();

下一步,我们设置正文/背景色 。

因为这个操作可以在程序中的其他部分执行,我把这些代码放到一个叫SetColor的函数里。

void SetColor()

{

CHARFORMAT cfm;

SendMessage(hwndRichEdit,EM_SETBKGNDCOLOR,0,BackgroundColor);

设置RichEdit控件的背景色是一个很简单的操作:只需发送 EM_SETBKGNDCOLOR

信息给RIchEdit控件就行了。(如果你使用多行Edit控件,你必须处理 WM_CTLCOLOREDIT

消息)。缺省的背景色是白色的。

RtlZeroMemory(&cfm,sizeof(cfm));

cfm.cbSize = sizeof(cfm);

cfm.dwMask = CFM_COLOR;

cfm.crTextColor = TextColor;

设置好背景色之后,我们填充 CHARFORMAT 的成员,以便用来设置设置正文颜色。应该注意的是我们使用该结构的大小来填充cbSize

成员,这样RichEdit 控件就知道我们发送的是CHARFORMAT, 而不是 CHARFORMAT2。 dwMask 只使用了一个

CFM_COLOR 标志, 因为我们只想设置正文颜色,同时往 crTextColor

里填入我们想要的正文颜色值.

SendMessage(hwndRichEdit,EM_SETCHARFORMAT,SCF_ALL,(LPARAM)&cfm);

}

设置好颜色后,你必须要清空 Undo

缓冲区,因为更改正文/背景颜色的操作是可撤消的(Undo-able),更改颜色时在缓冲区里留下了Undo信息。我们可以发送

EM_EMPTYUNDOBUFFER 消息来实现这个操作。

SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER,0,0 );

填充好 CHARFORMAT 结构, 我们发送 EM_SETCHARFORMAT 消息给RichEdit控件,在wParam中指定

SCF_ALL 标志,说明我们想把正文格式应用于控件中的所有正文。

注意在我们第一次创建RichEdit控件时,我们没有指定它的大小/位置。这是因为我们想它覆盖父窗口的全部客户区。当父窗口大小改变时,我们就跟着改变RichEdit控件的大小。

case WM_SIZE:

MoveWindow(hwndRichEdit,0,0,LOWORD(lParam),HIWORD(lParam),TRUE);

break;

在上面的程序片段, 我们使用了在 lParam中的客户区的新尺寸,通过 MoveWindow来改变RichEdit的大小

当用户点击文件 File/Edit 菜单条时,我们处理 WM_INITPOPUPMENU

消息,因此我们可以在显示子菜单给用户之前准备好各个子菜单项的状态。譬如,如果已经有一个文件在RichEdit控件中打开了,我们想禁止Open菜单项同时使能其他的菜单项。

对于这种情况下的File菜单条, 我们使用变量 FileOpened

来作为标志表示是否有一个文件已经打开了。如果这个变量是TRUE值,我们知道已经有一个文件被打开了。

case WM_INITMENUPOPUP:

if(LOWORD(lParam) == 0)

{

if(FileOpened)

{

EnableMenuItem((HMENU)wParam,IDM_OPEN,MF_GRAYED);

EnableMenuItem((HMENU)wParam,IDM_CLOSE,MF_ENABLED);

EnableMenuItem((HMENU)wParam,IDM_SAVE,MF_ENABLED);

EnableMenuItem((HMENU)wParam,IDM_SAVEAS,MF_ENABLED);

}

else

{

EnableMenuItem((HMENU)wParam,IDM_OPEN,MF_ENABLED);

EnableMenuItem((HMENU)wParam,IDM_CLOSE,MF_GRAYED);

EnableMenuItem((HMENU)wParam,IDM_SAVE,MF_GRAYED);

EnableMenuItem((HMENU)wParam,IDM_SAVEAS,MF_GRAYED);

}

}

正如你所见的,如果有一个文件已经打开了,我们将Open菜单项变灰禁止并将其他菜单项都使能。跟TRUE值相反的是 FileOpened

值为FALSE.

在这种情况下的EDIT菜单条我们需要先检查RichEdit控件/剪贴板的状态。

if(SendMessage(hwndRichEdit,EM_CANPASTE,CF_TEXT,0))

EnableMenuItem((HMENU)wParam,IDM_PASTE,MF_ENABLED);

else

EnableMenuItem((HMENU)wParam,IDM_PASTE,MF_GRAYED);

我们首先发送 EM_CANPASTE 消息,来检查剪贴板里是否存在可用的正文。如果有的话,SendMessage 返回 TRUE

,我们就将 Paste 菜单项使能。如果没有的话,我们将该菜单项变灰禁止。

if(SendMessage(hwndRichEdit,EM_CANUNDO,0,0))

EnableMenuItem((HMENU)wParam,IDM_UNDO,MF_ENABLED);

else

EnableMenuItem((HMENU)wParam,IDM_UNDO,MF_GRAYED);

过发送 EM_CANUNDO 消息来检查Undo 缓冲区是否为空,如果不空,SendMessage 返回 TRUE ,我们就使能

Undo 菜单项。

if(SendMessage(hwndRichEdit,EM_CANREDO,CF_TEXT,0))

EnableMenuItem((HMENU)wParam,IDM_REDO,MF_ENABLED);

else

EnableMenuItem((HMENU)wParam,IDM_REDO,MF_GRAYED);

我们通过发送 EM_CANREDO 消息给 RichEdit 控件来检查 Redo 缓冲区。如果不空的话,SendMessage 返回

TRUE,我们就使能 Redo 菜单项。

SendMessage(hwndRichEdit,EM_EXGETSEL,0,(LPARAM)&chrg);

if(chrg.cpMin == chrg.cpMax)

{

EnableMenuItem((HMENU)wParam,IDM_COPY,MF_GRAYED);

EnableMenuItem((HMENU)wParam,IDM_CUT,MF_GRAYED);

EnableMenuItem((HMENU)wParam,IDM_DELETE,MF_GRAYED);

}

else

{

EnableMenuItem((HMENU)wParam,IDM_COPY,MF_ENABLED);

EnableMenuItem((HMENU)wParam,IDM_CUT,MF_ENABLED);

EnableMenuItem((HMENU)wParam,IDM_DELETE,MF_ENABLED);

}

最后,我们通过发送 EM_EXGETSEL 消息来检查是否存在当前选定的正文,该消息使用一个 CHARRANGE

结构,定义如下:typedef struct _charrange {

LONG cpMin;

LONG cpMax;

} CHARRANGE;

cpMin 包含紧接在范围中的第一个字符之前的字符的位置索引。

cpMax 包含紧跟在范围中的最后一个字符之后的的字符的位置索引。

EM_EXGETSEL 返回后,CHARRANGE 结构就会被用选择范围的开始和结束位置索引所填充。如果没有当前选定,cpMin 和

cpMax 就会是同样的数值,我们就将 Cut/Copy/Delete 菜单项变灰禁止。

当用户点击 Open 菜单项,我们就显示一个打开文件的对话框,如果用户选择了一个文件,我们就打开该文件并将其内容流入RichEdit

控件中。

hFile =

CreateFile(FileName,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);

if(hFile != INVALID_HANDLE_VALUE)

{

editstream.dwCookie = (DWORD)hFile;

editstream.pfnCallback = StreamInProc;

SendMessage(hwndRichEdit,EM_STREAMIN,SF_TEXT,(LPARAM)&editstream);

使用 CreateFile成功打开文件后,我们填充EDITSTREAM 结构,以便准备发送 EM_STREAMIN 消息我们选择通过

dwCookie 成员发送打开文件的句柄,并在 pfnCallback 成员中传递流回调函数的地址。

流回调函数本身是简单基本的。

DWORD CALLBACK StreamInProc(DWORD hFile,LPBYTE pBuffer,long

NumBytes,long *pBytesRead)

{

return

(ReadFile((HANDLE)hFile,pBuffer,NumBytes,(LPDWORD)pBytesRead,0) ^

1);

}

你可以看到流回调函数的所有参数跟 ReadFile 函数 完美匹配。而且ReadFile 的返回值是是跟 1 经过

XOR异或运算的,因此如果它返回 1 (成功),实际返回的值的是0,反之亦然。

SendMessage(hwndRichEdit,EM_SETMODIFY,FALSE,0);

CloseHandle(hFile);

FileOpened = TRUE;

}消息EM_STREAMIN 返回后, 意味着流操作已经完成。实际上,我们必需检查 EDITSTREAM 结构的 dwError

成员。

RichEdit (和 Edit) 控件支持一个标志,用来指示其内容时候被改变了。我们可以通过发送 EM_GETMODIFY

消息给控件来得到这个标志的值。

如果控件内容被改变了的话,SendMessage 返回

TRUE。因为我们将正文流入控件,也是一种改变。所以我们必需设置修改标志为FALSE,

可以通过给控件发送EM_SETMODIFY 消息,令其 wParam==FALSE

使控件的修改标志在流入操作完成后重新开始。然后我们马上就关闭文件并设置变量 FileOpened 为 TRUE

来说明已经打开了一个文件。

当用户点击 Save/SaveAs 菜单项,我们使用 EM_STREAMOUT

消息来将RichEdit控件的内容输出到一个文件中。跟流入回调函数一样,流出回调函数本身也是很简单的。它也是跟

WriteFile完美匹配.

正文操作象Cut/Copy/Paste/Redo/Undo 等通过发送单个消息给控件是很容易实现的,这些消息分别是

WM_CUT/WM_COPY/WM_PASTE/WM_REDO/WM_UNDO 。

删除/选择全部正文的操作如下:

case IDM_DELETE:

SendMessage(hwndRichEdit,EM_REPLACESEL,TRUE,0);

break;

case IDM_SELECTALL:

chrg.cpMax = -1;

chrg.cpMin = 0;

SendMessage(hwndRichEdit,EM_EXSETSEL,0,(LPARAM)&chrg);

break;

删除操作影响到当前的选定。我发送 EM_REPLACESEL 消息,传递一个 NULL 字符串,RichEdit

控件将会用空串来替代当前选定的正文串。

选择全部正文的操作通过发送 EM_EXSETSEL 消息,指定 cpMin==0 和 cpMax==-1

的数值来选择所有的正文。

当用户选择 Option 菜单条,我们显示一个对话框,显示当前的背景/正文颜色。

a4c26d1e5885305701be709a3d33442f.png

当用户点击任意一个颜色框,它会显示颜色选择对话框。"颜色框" 实际上是一个具有 SS_NOTIFY 和 WS_BORDER

风格标志的静态控件。具有 SS_NOTIFY 标志的静态控件会将在上面的鼠标动作通知给父窗口,譬如BN_CLICKED

(STN_CLICKED). 这个就是窍门了!

case IDC_BACKCOLORBOX:

RtlZeroMemory(&clr,sizeof(clr));

clr.lStructSize = sizeof(clr);

clr.hwndOwner = hWnd;

clr.hInstance = (HWND)g_hInstance;

clr.rgbResult = BackgroundColor;

clr.lpCustColors = CustomColors;

clr.Flags = CC_ANYCOLOR | CC_RGBINIT;

if(ChooseColor(&clr))

{

BackgroundColor = clr.rgbResult;

InvalidateRect(GetDlgItem(hWnd,IDC_BACKCOLORBOX),0,TRUE);

}

break;

当用户点击任意一个颜色框时,我们会填充 CHOOSECOLOR 结构的成员并调用ChooseColor

来显示颜色选择对话框。如果用户选择了一种颜色,颜色值会在 rgbResult 成员中返回,我们把改值保存在变量

BackgroundColor 中。之后,我们通过用颜色框句柄调用InvalidateRect 函数来强迫颜色框进行重画。

颜色框会发送 WM_CTLCOLORSTATIC 消息给父窗口.

if(GetDlgItem(hWnd,IDC_BACKCOLORBOX) ==

(HWND)lParam)

{

return (int)CreateSolidBrush(BackgroundColor);

}

在 WM_CTLCOLORSTATIC 消息处理中,我们把在 lParam

传递进来的静态控件的句柄跟那两个颜色框的句柄作比较。如果值匹配的话,我们就使用变量中颜色值来创建一个新画刷并立刻返回。静态控件将会使用新创建的画刷来重画其背景。

关于RichEdit控件基础知识方面的东西,我们就为大家讲解到这里吧,我们今天的继续讲解RichEdit控件基础知识之二window

API 编程 c语言实现 第三十七讲就先说到这里哦

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值