windows sdk编程系列文章

更新时间:2023-10-18 17:15:01 阅读量: 综合文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

windows sdk编程系列文章 ---- RichEdit控件基础知识之四 2008-05-08 09:21

分析:

例子程序首先载入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 传递进来的静态控件的句柄跟那两个颜色框的句柄作比较。如果值匹配的话,我们就使用变量中颜色值来创建一个新画刷并立刻返回。静态控件将会使用新创建的画刷来重画其背景。

技术成就梦想

类别:windows sdk开发 |

| 添加到搜藏 | 分享到i贴吧 | 浏览(631)

| 评论 (0)

上一篇:windows sdk编程系列文章 ---- R... 下一篇:windows sdk编程系列文章 ---- R...

相关文章:

? VC中在对话框上使用Rich Edit控... ? RichEdit控件加载背景图片 - 转如何在 Visual C++6.0 中使用 ? ? RichEdit控件如何响应鼠标单击??Ri...

? RichEdit 控件基础知识 ? RichEdit控件参考手册

类似QQ的表情,在RichEdit控件

? RichEdit控件中插入图片 ?

中...

查找QQ输入聊天内容的RichEditvb6里TextBox烦心的地方……又? ? 控... 被...更多>>

最近读者:

登录后,您就出现在这里。

zhangxl1菜菜结lctqzq

17晶

本文来源:https://www.bwwdw.com/article/u0xf.html

Top