今天的这个课程,我们继续为各位朋友们讲解关于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 菜单条,我们显示一个对话框,显示当前的背景/正文颜色。
当用户点击任意一个颜色框,它会显示颜色选择对话框。"颜色框" 实际上是一个具有 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语言实现 第三十七讲就先说到这里哦