Bootstrap

《Windows API每日一练》11.4 Richedit控件

面的章节中,我们学习了edit编辑控件,本节我们将学习一种功能更为强大的编辑控件——富文本控件richedit控件。

本节必须掌握的知识点:

        富文本控件

        第73练:富文本控件

11.4.1 富文本控件

RichEdit 控件是Windows操作系统提供的一个功能丰富的文本编辑控件,它支持格式化文本、插入图片、链接、表格等功能。RichEdit 控件可以用于创建具有高级文本编辑功能的应用程序,如文本编辑器、电子邮件客户端等。

以下是 RichEdit 控件的一些特性和功能:

●文本格式化:RichEdit 控件支持多种文本格式,包括字体、颜色、大小、样式(如粗体、斜体、下划线等)、段落格式(如对齐方式、缩进等)等。开发人员可以通过控件的接口或消息来设置和修改文本的格式。

●图片插入:RichEdit 控件允许在文本中插入图片,以丰富文本内容。可以通过控件的接口或消息来插入、删除、调整图片的位置和大小等。

●超链接:RichEdit 控件支持文本中的超链接,可以将文本或图片转换为可点击的链接,点击链接时可以执行相应的操作,如打开网址、跳转到其他部分等。

●表格:RichEdit 控件提供创建和编辑表格的功能。可以插入表格、调整行列的大小、合并单元格等。

●撤销和重做:RichEdit 控件支持撤销和重做操作,用户可以撤销之前的编辑操作或者重做已撤销的操作。

●导入和导出:RichEdit 控件支持导入和导出文本内容,可以将文本保存为不同的文件格式,如RTF(Rich Text Format)、HTML、纯文本等。

●编辑事件和通知:RichEdit 控件提供了一系列的事件和通知机制,开发人员可以通过处理这些事件或者捕捉通知消息来响应用户的编辑操作,实现自定义的交互逻辑。

RichEdit 控件在不同的 Windows 版本中有不同的实现和功能支持。常见的 RichEdit 控件版本包括 RichEdit 1.0、RichEdit 2.0、RichEdit 3.0 等。每个版本的 RichEdit 控件都有相应的接口和消息来操作和控制控件的行为。

在使用 RichEdit 控件时,可以通过代码创建控件实例并进行初始化,也可以在资源编辑器中添加 RichEdit 控件并在程序中获取该控件的句柄进行操作。通过控件的接口、属性或消息,可以设置文本格式、插入图片、处理编辑事件等。

RichEdit 控件的消息处理

       RichEdit 控件的消息处理可以通过以下几种方式进行:

●响应窗口消息:RichEdit 控件是通过窗口消息进行交互的,因此可以在窗口过程中处理 RichEdit 相关的消息。在窗口过程中,可以使用 switch-case 语句来捕获并处理 RichEdit 控件发送的消息,如 WM_COMMAND、WM_NOTIFY 等。根据消息的类型和参数,可以执行相应的操作,例如处理文本编辑事件、处理超链接点击等。

●子类化窗口:可以将 RichEdit 控件进行子类化,通过设置控件的新窗口过程来拦截和处理 RichEdit 控件的消息。通过子类化,可以自定义控件的行为和响应逻辑,例如处理特定的键盘事件、自定义绘制等。

●使用控件提供的接口:RichEdit 控件一般提供了一系列的接口函数,可以通过这些接口函数直接与控件进行交互。通过调用接口函数,可以设置文本格式、插入图片、获取和修改文本内容等。具体的接口函数名称和使用方法可以参考相应的控件文档。

下面是一个使用窗口消息处理 RichEdit 控件的示例:

       LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)

{

    switch (uMsg)

    {

        case WM_COMMAND:

        {

            // 处理 RichEdit 控件的命令消息

            if (LOWORD(wParam) == IDC_RICHEDIT)

            {

                switch (HIWORD(wParam))

                {

                    case EN_CHANGE:

                        // RichEdit 控件的内容发生变化

                        // 执行相应的处理逻辑

                        break;

                    case EN_LINK:

                        // RichEdit 控件的超链接被点击

                        // 执行相应的处理逻辑

                        break;

                    // 其他 RichEdit 控件的命令消息

                }

            }

            break;

        }

        case WM_NOTIFY:

        {

            // 处理 RichEdit 控件的通知消息

            NMHDR* pnmhdr = (NMHDR*)lParam;

            if (pnmhdr->idFrom == IDC_RICHEDIT)

            {

                switch (pnmhdr->code)

                {

                    case EN_SELCHANGE:

                        // RichEdit 控件的选中文本发生变化

                        // 执行相应的处理逻辑

                        break;

                    // 其他 RichEdit 控件的通知消息

                }

            }

            break;

        }

        // 其他窗口消息的处理

    }

    return DefWindowProc(hWnd, uMsg, wParam, lParam);

}

在上述示例中,通过捕获 WM_COMMAND 和 WM_NOTIFY 消息,并根据消息的参数进行判断,可以处理 RichEdit 控件的命令消息和通知消息。根据具体的消息类型和参数,可以执行相应的处理逻辑。

11.4.2 第73练:富文本控件

/*------------------------------------------------------------------------

 073 WIN32 API 每日一练

     第73个例子Richedit.c:通用控件-Richedit富文本编辑控件

     FINDREPLACE查找对话框结构

     EDITSTREAM富文本控件消息结构

     CHARFORMAT Rich Edit 控件中字符格式结构

     LoadLibrary函数

     WM_ACTIVATE激活消息

     WM_INITMENU初始化菜单消息

 (c) www.bcdaren.com 编程达人

-----------------------------------------------------------------------*/

#include <Windows.h>

#include <commctrl.h>

#pragma comment(lib,"comctl32.lib")//通用控件库

#include "resource.h"

#include <richedit.h>//富文本控件

HWND hWinMain,hWinEdit,hFindDialog;

TCHAR szCaptionMain[] = TEXT("记事本"); // 窗口窗口标题

int idFindMessage;

TCHAR szFindText[100];

HANDLE hFile;

HMENU hMenu;

FINDREPLACE stFind = { sizeof(FINDREPLACE), 0, 0, FR_DOWN, szFindText,

    0, sizeof(szFindText), 0, 0, 0, 0 };//初始化查找对话框,默认向下查找

void _SaveFile();

void _OpenFile();

DWORD CALLBACK _ProcStream(DWORD _dwCookie, LPVOID _lpBuffer, DWORD _dwBytes, LPDWORD _lpBytes);

void _Init();

void _Quit();

void _SetStatus();

void _FindText();

BOOL _CheckModify();

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,

    LPSTR lpCmdLine, int nShowCmd)//默认填写

{

    static TCHAR szClassName[] = TEXT("Wordpad"); // 类名

    static TCHAR szDllEdit[] = TEXT("RichEd20.dll"); // Richedit 2.0版本

    MSG msg;      //MSG 结构变量

    WNDCLASSEX wndclass; //WNDCLASSEX结构变量

    HACCEL hAccelerator;

    HMODULE hRichEdit;

    //动态加载DLL链接库

    hRichEdit = LoadLibrary(szDllEdit);

   

    //初始化窗口类

    wndclass.style = CS_HREDRAW | CS_VREDRAW;//重叠窗口

    wndclass.lpfnWndProc = WndProc;//回调函数指针

    wndclass.cbClsExtra = 0;

    wndclass.cbWndExtra = 0;

    wndclass.hbrBackground = (HBRUSH)GetStockObject(COLOR_BTNFACE + 1);//背景色

    wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);

    wndclass.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(ICO_MAIN));

    wndclass.lpszMenuName = NULL;//菜单

    wndclass.hInstance = hInstance;//实例句柄

    wndclass.cbSize = sizeof(WNDCLASSEX);

    wndclass.hIconSm = NULL;

    wndclass.lpszClassName = szClassName;

    if (!RegisterClassEx(&wndclass))

    {

        MessageBox(0, TEXT("This program requires Windows NT!"),

            szClassName, MB_ICONERROR);

        return 0;

    }

    hMenu = LoadMenu(hInstance, MAKEINTRESOURCE(IDR_MENU1));

    hWinMain = CreateWindowEx(WS_EX_CLIENTEDGE,

        szClassName,    //窗口类名

        szCaptionMain//窗口标题

        WS_OVERLAPPEDWINDOW,     //窗口样式

        CW_USEDEFAULT, CW_USEDEFAULT, 600, 400,

        NULL,       // 父窗口句柄

        hMenu,      // 窗口菜单句柄

        hInstance, // 程序实例句柄

        NULL);      // 创建参数

    ShowWindow(hWinMain, nShowCmd); //显示窗口

    UpdateWindow(hWinMain);

    hAccelerator = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDA_MAIN));

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

    {

        if (TranslateAccelerator(hWinMain,hAccelerator,&msg) == 0)

        {

            TranslateMessage(&msg);

            DispatchMessage(&msg);

        }

    }

    FreeLibrary(hRichEdit);//释放动态链接库

    return msg.wParam;

}

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)

{

    static TCHAR szClass[] = TEXT("EDIT"); // 类名

    static TCHAR szCaption[] = TEXT("命令消息"); // 窗口窗口标题

    static TCHAR szFormat[] = TEXT("收到 WM_COMMAND 消息,命令ID:%d");

    static TCHAR szBuffer[128];

    CHARRANGE stRange//头文件richedit.h

    RECT rect;

    HWND tmp;

    switch (message)

    {

    case WM_CREATE:

        hWinMain = hWnd;

        _Init();

        return 0;

    case WM_SIZE:

        GetClientRect(hWinMain, &rect);

        MoveWindow(hWinEdit, 0, 0, rect.right, rect.bottom, TRUE);

        return 0;

/*方法1:

    case WM_INITMENUPOPUP:

        switch (lParam)

        {

        case 0: //文件菜单

//启用,禁用或显示指定的菜单项

            EnableMenuItem((HMENU)wParam, IDM_SAVE,

//获取编辑控件的修改标志的状态

                SendMessage(hWinEdit, EM_GETMODIFY, 0, 0L) ?

                MF_ENABLED : MF_GRAYED);

            break;

        case 1: //编辑菜单

            SendMessage(hWinEdit, EM_EXGETSEL, 0, (LPARAM)&stRange);

            EnableMenuItem(hMenu, IDM_COPY, stRange.cpMin == stRange.cpMax ?

MF_GRAYED : MF_ENABLED);

            EnableMenuItem(hMenu, IDM_CUT, MF_ENABLED);

            EnableMenuItem(hMenu, IDM_PASTE,

                SendMessage(hWinEdit, EM_CANPASTE, 0, 0) ? MF_ENABLED :

MF_GRAYED);

            EnableMenuItem(hMenu, IDM_REDO,

                SendMessage(hWinEdit, EM_CANREDO, 0, 0) ? MF_ENABLED :

 MF_GRAYED);

            EnableMenuItem(hMenu, IDM_UNDO,

                SendMessage(hWinEdit, EM_CANUNDO, 0, 0) ? MF_ENABLED :

MF_GRAYED);

            EnableMenuItem(hMenu, IDM_SELALL,

                GetWindowTextLength(hWinEdit) ? MF_ENABLED : MF_GRAYED);

            EnableMenuItem(hMenu, IDM_FINDNEXT, szFindText[0] ? MF_ENABLED :

MF_GRAYED);

            EnableMenuItem(hMenu, IDM_FINDPREV, szFindText[0] ? MF_ENABLED :

MF_GRAYED);

            break;

        }

        return 0;

*/

    /*方法2:*/

    case WM_INITMENU:

        _SetStatus();

        return 0;

    case WM_COMMAND:

        switch (LOWORD(wParam))

        {

        case IDM_EXIT:

            DestroyWindow(hWinMain);

            PostQuitMessage(0);

            break;

        case IDM_OPEN:

            if (_CheckModify())

                _OpenFile();

            break;

        case IDM_SAVE:

            _SaveFile();

            break;

        case IDM_UNDO:

            SendMessage(hWinEdit,EM_UNDO,0,0);

            break;

        case IDM_REDO:

            SendMessage(hWinEdit, EM_REDO, 0, 0);

            break;

        case IDM_SELALL:

//如果cpMin和cpMax成员相等,则范围为空。如果cpMin为0并且cpMax为–1,

//则该范围包括所有内容

            stRange.cpMin = 0;

            stRange.cpMax = -1;

            SendMessage(hWinEdit, EM_EXSETSEL, 0, (LPARAM)&stRange);

            break;

        case IDM_COPY:

            SendMessage(hWinEdit, WM_COPY, 0, 0);

            break;

        case IDM_CUT:

            SendMessage(hWinEdit, WM_CUT, 0, 0);

            break;

        case IDM_PASTE:

            SendMessage(hWinEdit, WM_PASTE, 0, 0);

            break;

        case IDM_FIND:

            stFind.Flags &= (~FR_DIALOGTERM);//设置除FR_DIALOGTERM之外所有标志

            tmp = FindText(&stFind);//创建查找对话框,返回对话框句柄

            if (tmp)

                hFindDialog = tmp;

            break;

        case IDM_FINDPREV:

            stFind.Flags &= (~FR_DOWN);//向上查找

            _FindText();

            break;

        case IDM_FINDNEXT:

            stFind.Flags |= FR_DOWN;//向下查找

            _FindText();

            break;

        }

        return 0;

    //激活消息:发送到正在激活的窗口和正在停用的窗口

    case WM_ACTIVATE:   //鼠标激活或者鼠标以外激活

        if ((LOWORD(wParam)) == WA_CLICKACTIVE || (LOWORD(wParam)) == WA_ACTIVE)//

            SetFocus(hWinEdit);

        return 0;

    case WM_DESTROY:

        PostQuitMessage(0);

        return 0;

    default:

        if (message == idFindMessage)

        {

            if (stFind.Flags & FR_DIALOGTERM)//检测到对话框关闭

                hFindDialog = 0;

            else

                _FindText();

        }

        return DefWindowProc(hWnd, message, wParam, lParam);

    }

}

void _SaveFile()

{

    EDITSTREAM stES;    //富文本控件消息结构

    SetFilePointer(hFile, 0, 0, FILE_BEGIN);//移动文件指针到开头

//截断文件,将指定文件的物理文件大小设置为文件指针的当前位置

    SetEndOfFile(hFile);

    stES.dwCookie = FALSE;//传递给回调函数的标记值,FALSE写操作

    stES.pfnCallback = (EDITSTREAMCALLBACK)_ProcStream;//回调函数

//指示富文本控件将文本内容传递给回调函数用于输出

    SendMessage(hWinEdit,EM_STREAMOUT,SF_TEXT,(LPARAM)&stES);

//设置或清除编辑控件的修改标志,TRUE表示文本已修改,FALSE表示未修改该文本

    SendMessage(hWinEdit,EM_SETMODIFY,FALSE,0);

}

void _OpenFile()

{

    OPENFILENAME stOF;

//富编辑控件的信息,Rich Edit控件使用该信息将数据流传输到控件中或从控件中传出。

    EDITSTREAM stES;

    static TCHAR szFileName[MAX_PATH];

    static TCHAR szDefaultExt[] = TEXT("txt");

    static TCHAR szErrOpenFile[] = TEXT("无法打开文件!");

    static TCHAR szFilter[] = TEXT("Text Files(*.txt)\0*.txt\0All

Files(*.*)\0*.*\0\0");

    //显示打开文件对话框

    RtlZeroMemory(&stOF,sizeof(stOF));

    stOF.lStructSize = sizeof(stOF);

    stOF.hwndOwner = hWinMain;

    stOF.lpstrFilter = szFilter;

    stOF.lpstrFile = szFileName;

    stOF.nMaxFile = MAX_PATH;

    stOF.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST;

    stOF.lpstrDefExt = szDefaultExt;

    if (GetOpenFileName(&stOF))

    {

        if (hFile)

            CloseHandle(hFile);

        hFile = CreateFile(szFileName, GENERIC_READ |GENERIC_WRITE,

            FILE_SHARE_READ |FILE_SHARE_WRITE, NULL, OPEN_EXISTING,

FILE_ATTRIBUTE_NORMAL, NULL);

        if (hFile == INVALID_HANDLE_VALUE)

        {

            MessageBox(hWinMain,szErrOpenFile,NULL,MB_OK | MB_ICONSTOP);

            return;

        }

        //装入和保存文本

        stES.dwCookie = TRUE;

        stES.pfnCallback = (EDITSTREAMCALLBACK)_ProcStream;//回调函数

//指示回调函数读入文本

        SendMessage(hWinEdit,EM_STREAMIN,SF_TEXT,(LPARAM)&stES);

//设置或清除编辑控件的修改标志-未修改

        SendMessage(hWinEdit,EM_SETMODIFY,FALSE,0);

    }

}

//Richedit文本控件输入和输出回调函数

DWORD CALLBACK _ProcStream(DWORD _dwCookie, LPVOID _lpBuffer, DWORD _dwBytes, LPDWORD _lpBytes)

{

    if (_dwCookie)

        ReadFile(hFile, _lpBuffer, _dwBytes, _lpBytes, 0);

    else

        WriteFile(hFile, _lpBuffer, _dwBytes, _lpBytes, 0);

    return 0;

}

void _Init()

{

    CHARFORMAT stCF;//包含有关 Rich Edit 控件中的字符格式的信息

    static TCHAR szClassEdit[] = TEXT("RichEdit20W");   //UNICODE版本

    //static TCHAR szClassEdit[] = TEXT("RichEdit20A"); //ASCII码版本

    static TCHAR szFont[] = TEXT("宋体");

    //注册“查找”对话框消息,初始化“查找”对话框结构

    stFind.hwndOwner = hWinMain;

    idFindMessage = RegisterWindowMessage(FINDMSGSTRING);

    //建立输出文本窗口

    hWinEdit = CreateWindowEx(WS_EX_CLIENTEDGE,szClassEdit,NULL,

        WS_CHILD| WS_VISIBLE | WS_VSCROLL | WS_HSCROLL | ES_MULTILINE |

ES_NOHIDESEL,

        0,0,0,0,hWinMain,0,GetModuleHandle(0),NULL);

    SendMessage(hWinEdit,EM_SETTEXTMODE,TM_PLAINTEXT,0);//纯文本模式

    RtlZeroMemory(&stCF,sizeof(stCF));

    stCF.cbSize = sizeof(stCF);

    stCF.yHeight = 9 * 20;  //以缇为单位(1/1440英寸或打印机点的1/20)

    stCF.dwMask = (CFM_FACE | CFM_SIZE | CFM_BOLD);//CFE_BOLD

    lstrcpy(stCF.szFaceName,szFont);//字体名称

    SendMessage(hWinEdit,EM_SETCHARFORMAT,0,(LPARAM)&stCF);//设置字符格式的信息

    //64K 个字符,在调用 EM _ EXLIMITTEXT 之前,

//用户可输入的文本量的默认限制为32767个字符

    SendMessage(hWinEdit,EM_EXLIMITTEXT,0,-1);

}

void _Quit()

{

    if (_CheckModify())

    {

        DestroyWindow(hWinMain);

        PostQuitMessage(0);

    }

    if (hFile)

        CloseHandle(hFile);

}

void _SetStatus()

{

    //根据情况改变菜单状况

    CHARRANGE stRange;//指定 Rich Edit 控件中的字符范围

    SendMessage(hWinEdit,EM_EXGETSEL,0, (LPARAM)&stRange);

    if (stRange.cpMin == stRange.cpMax)

    {

        EnableMenuItem(hMenu, IDM_COPY, MF_GRAYED);

        EnableMenuItem(hMenu, IDM_CUT, MF_ENABLED);

    }

    else

    {

        EnableMenuItem(hMenu, IDM_COPY, MF_ENABLED);

        EnableMenuItem(hMenu, IDM_CUT, MF_ENABLED);

    }

    if (SendMessage(hWinEdit, EM_CANPASTE, 0, 0))

            EnableMenuItem(hMenu, IDM_PASTE, MF_ENABLED);

    else

        EnableMenuItem(hMenu, IDM_PASTE, MF_GRAYED);

    if (SendMessage(hWinEdit, EM_CANREDO, 0, 0))

        EnableMenuItem(hMenu, IDM_REDO, MF_ENABLED);

    else

        EnableMenuItem(hMenu, IDM_REDO, MF_GRAYED);

    if (SendMessage(hWinEdit, EM_CANUNDO, 0, 0))

            EnableMenuItem(hMenu, IDM_UNDO, MF_ENABLED);

    else

        EnableMenuItem(hMenu, IDM_UNDO, MF_GRAYED);

    if (GetWindowTextLength(hWinEdit))

            EnableMenuItem(hMenu, IDM_SELALL, MF_ENABLED);

    else

        EnableMenuItem(hMenu, IDM_SELALL, MF_GRAYED);

    //获取编辑控件修改标志的状态

    if (SendMessage(hWinEdit, EM_GETMODIFY, 0, 0))

            EnableMenuItem(hMenu, IDM_SAVE, MF_ENABLED);

    else

        EnableMenuItem(hMenu, IDM_SAVE, MF_GRAYED);

    if (szFindText[0])

    {

        EnableMenuItem(hMenu, IDM_FINDNEXT, MF_ENABLED);

        EnableMenuItem(hMenu, IDM_FINDPREV, MF_ENABLED);

    }

    else

    {

        EnableMenuItem(hMenu, IDM_FINDNEXT, MF_GRAYED);

        EnableMenuItem(hMenu, IDM_FINDPREV, MF_GRAYED);

    }

}

void _FindText()

{

    FINDTEXTEX stFindText;

    static TCHAR szNotFound[] = TEXT("字符串未找到!");

    //设置查找范围--要搜索的字符范围。要在整个控制向前搜索,

//设置cpMin为0,cpMax就会为-1

    SendMessage(hWinEdit,EM_EXGETSEL,0, (LPARAM)&stFindText.chrg);

    if (stFind.Flags & FR_DOWN)

        stFindText.chrg.cpMin = stFindText.chrg.cpMax;

    stFindText.chrg.cpMax = -1;//改为向前查找

    //设置查找选项

    stFindText.lpstrText = szFindText;

//匹配大小写,向下查找,全字匹配

    stFind.Flags &= (FR_MATCHCASE | FR_DOWN | FR_WHOLEWORD);

    //查找并把光标设置到找到的文本上

    if (SendMessage(hWinEdit, EM_FINDTEXTEX, stFind.Flags,

(LPARAM)&stFindText) == -1)

    {

        if (hFindDialog)

        {

            hWinMain = hFindDialog;

        }

        else

            MessageBox(hWinMain, szNotFound, NULL, MB_OK | MB_ICONINFORMATION);

        return;        

    }

//设置选定内容

    SendMessage(hWinEdit,EM_EXSETSEL,0, (LPARAM)&stFindText.chrgText);

    SendMessage(hWinEdit,EM_SCROLLCARET,0,0);//重新定位滑块位置到找到的文本位置

}

BOOL _CheckModify()

{

    //页面设置对话框

    BOOL dwReturn;

    static TCHAR szModify[] = TEXT("文件已修改,是否保存?");

    int click;

    dwReturn = TRUE;

//指示编辑控件的内容是否已被修改,内容已被修改,则返回值为非零值;否则为零

    if (SendMessage(hWinEdit,EM_GETMODIFY,0,0) && hFile)

    {

        click = MessageBox(hWinMain,szModify, szCaptionMain,MB_YESNOCANCEL |

MB_ICONQUESTION);

        if (click == IDYES)

            _SaveFile();

        else if (click == IDCANCEL)

            dwReturn = FALSE;

    }

    return dwReturn;

}

/*****************************************************************************

EDITSTREAM是一个结构体,用于在 RichEdit 控件中进行文本流操作,包括读取和写入文本内容。

它被用作 RichEdit 控件发送 EM_STREAMIN 和 EM_STREAMOUT 消息时的参数。

EDITSTREAM 结构体的定义如下:

typedef struct _editstream {

  DWORD_PTR dwCookie;       // 应用程序定义的值,用于传递额外的数据

  DWORD     dwError;        // 接收操作的错误代码

  EDITSTREAM pfnCallback;   // 回调函数指针,用于处理流操作

} EDITSTREAM, *LPEDITSTREAM;

回调函数的声明如下:

DWORD CALLBACK EditStreamCallback(DWORD_PTR dwCookie,

                LPBYTE pbBuff, LONG cb, LONG *pcb);

回调函数的参数包括:

dwCookie:传递给 EDITSTREAM 结构体的 dwCookie 成员,用于传递额外的数据。

pbBuff:指向缓冲区的指针,用于读取或写入数据。

cb:缓冲区的大小,表示回调函数可以读取或写入的最大字节数。

pcb:指向 LONG 类型的指针,用于返回实际读取或写入的字节数。

*****************************************************************************

CHARFORMAT 是一个结构体,用于在 RichEdit 控件中设置或获取文本格式的属性。

它被用作 RichEdit 控件发送 EM_GETCHARFORMAT 和 EM_SETCHARFORMAT 消息时的参数。

CHARFORMAT 结构体的定义如下:

typedef struct _charformat {

  UINT    cbSize;           // 结构体的大小

  DWORD   dwMask;           // 标志位,指定要应用的属性

  DWORD   dwEffects;        // 文本效果,如粗体、斜体等

  LONG    yHeight;          // 字体高度

  LONG    yOffset;          // 基线偏移

  COLORREF crTextColor;     // 文本颜色

  BYTE    bCharSet;         // 字符集

  BYTE    bPitchAndFamily;  // 字体的间距和系列

  TCHAR   szFaceName[LF_FACESIZE];  // 字体名称

} CHARFORMAT, *LPCHARFORMAT;

*****************************************************************************

LoadLibrary函数是一个 Windows 平台上的函数,用于加载动态链接库(DLL)文件并返回一个模块句柄。

它可以用于在运行时动态加载 DLL 文件,并获取其中导出的函数、变量或资源。

LoadLibrary 函数的声明如下:

HMODULE LoadLibrary(

  LPCTSTR lpLibFileName  // DLL 文件的路径或名称

);

参数说明:

lpLibFileName:指定 DLL 文件的路径或名称。可以是绝对路径,也可以是相对于当前进程的路径。

可以使用字符串字面量或字符数组来表示 DLL 的路径或名称。

返回值:

如果加载 DLL 成功,函数将返回 DLL 的模块句柄(HMODULE)。该句柄用于后续操作,如获取导出函数地址等。

如果加载 DLL 失败,函数将返回 NULL。可以使用 GetLastError 函数获取详细的错误信息。

*****************************************************************************

WM_ACTIVATE消息:通知窗口或控件的活动状态发生了改变。当窗口从非活动状态转为活动状态,

或者从活动状态转为非活动状态时,系统会发送 WM_ACTIVATE 消息给相应的窗口过程函数。

WM_ACTIVATE 消息的常量值为 0x0006。

WM_ACTIVATE 消息的消息参数包括:

wParam:指定窗口的活动状态。可以是以下值之一:

WA_INACTIVE(0):窗口变为非活动状态。

WA_ACTIVE(1):窗口变为活动状态。

WA_CLICKACTIVE(2):窗口变为活动状态,并且用户通过鼠标点击激活了窗口。

lParam:指定窗口的句柄(HWND)和最小化状态。低字节指定最小化状态,可以是以下值之一:

0:窗口没有最小化。

1:窗口最小化。

WM_ACTIVATE 消息的处理通常在窗口过程函数中进行。窗口过程函数接收到 WM_ACTIVATE 消息后,

可以根据 wParam 和 lParam 的值来执行相应的操作。例如,可以根据窗口的活动状态来更新窗口的外观、

启用或禁用控件,或执行其他与窗口活动状态相关的逻辑。

*****************************************************************************

WM_INITMENU消息:它在一个窗口收到 WM_INITMENU 消息时发送给窗口过程函数,

通知窗口即将显示一个菜单。该消息用于初始化菜单,允许窗口在菜单显示之前进行一些处理。

WM_INITMENU 消息的常量值为 0x0116。

WM_INITMENU 消息的消息参数包括:

wParam:指定菜单的句柄(HMENU)。

lParam:为零。

WM_INITMENU 消息的处理通常在窗口过程函数中进行。窗口过程函数接收到 WM_INITMENU 消息后,

可以根据 wParam 的值执行相应的操作,如禁用或启用菜单项、更新菜单项的状态、动态添加菜单项等。

*/

       运行结果:

图11-4 Richedit控件

 

总结

       是否还记得我们在第五章实例TYPER.C创建了一个非常简单的文本编辑器,功能非常简单。接着在第九章edit控件实例POPPAD1.C中实现了一个功能强大的文本编辑器。实例Richedit.c创建的富文本控件则是Windows系统为我们提供的功能更为强大的文本编辑器。

       富文本控件除了需要添加通用控件库之外,还需要添加richedit.h头文件。接下来我们详细分析。

1.窗口过程处理WM_CREATE消息时,调用自定义函数_Init进行一些初始化的工作。注册了一个查找消息idFindMessage。调用CreateWindowEx函数创建一个"RichEdit20W"富文本控件,并完成富文本控件的初始化(CHARFORMAT结构初始化)。

2.WM_SIZE消息:调用MoveWindow函数调整窗口大小。

3.接着就是完成菜单项的初始化。可以有两种方法。第一种方法是通过WM_INITMENUPOPUP消息完成菜单初始化。调用EnableMenuItem函数禁用或启用菜单项。第二种方法是处理WM_INITMENU消息初始化菜单。

4.WM_COMMAND消息:出来了菜单消息,菜单的处理和之前的工具栏实例大致相同,此处不再赘述。

需要特别说明的是对于富文本控件的输入和输出操作。

EDITSTREAM 是一个结构体(structure),用于在 Windows 编辑控件中进行文本输入和输出的操作。它在富文本编辑控件(Rich Edit Control)中使用,用于指定和传递文本输入和输出的相关信息。

EDITSTREAM 结构体的定义如下:

typedef struct _editstream {

    DWORD_PTR dwCookie;   // 应用程序定义的数据指针,用于传递额外的信息

    DWORD     dwError;   // 出错代码,由编辑控件填充

    EDITSTREAMCALLBACK pfnCallback; // 回调函数指针,用于处理输入和输出操作

} EDITSTREAM, *LPEDITSTREAM;

使用 EDITSTREAM 结构体进行文本输入和输出的操作通常包括以下步骤:

1.创建一个 EDITSTREAM 结构体的实例,并设置 dwCookie 和 pfnCallback 成员。

2.将 EDITSTREAM 结构体传递给编辑控件的消息处理函数,如 EM_STREAMIN 或 EM_STREAMOUT 消息。

3.编辑控件在需要输入或输出文本时,通过回调函数调用应用程序提供的回调函数,并传递相应的数据。

4.在回调函数中,应用程序根据需要处理输入或输出的文本数据。可以读取输入数据并进行处理,或者将输出数据写入到 pbBuff 缓冲区中。

5.回调函数返回后,编辑控件将根据操作的结果更新 dwError 成员,应用程序可以通过该成员获取操作的结果。

在_OpenFile()函数中:

//富编辑控件信息。Rich Edit控件使用该信息将数据流传输到控件中或从控件中传出。

EDITSTREAM stES;

    //装入和保存文本

    stES.dwCookie = TRUE;

    stES.pfnCallback = (EDITSTREAMCALLBACK)_ProcStream;//回调函数

//指示回调函数读入文本

    SendMessage(hWinEdit,EM_STREAMIN,SF_TEXT,(LPARAM)&stES);

//设置或清除编辑控件的修改标志-未修改

SendMessage(hWinEdit,EM_SETMODIFY,FALSE,0);

//Richedit文本控件输入和输出回调函数

DWORD CALLBACK _ProcStream(DWORD _dwCookie, LPVOID _lpBuffer,

DWORD _dwBytes, LPDWORD _lpBytes)

{

    if (_dwCookie)

        ReadFile(hFile, _lpBuffer, _dwBytes, _lpBytes, 0);

    else

        WriteFile(hFile, _lpBuffer, _dwBytes, _lpBytes, 0);

    return 0;

}

【注意】实例添加了三个资源:快捷键、图标和菜单。

;