我正在尝试在RichEdit中实现文本高亮显示和撤消。下面的代码标记了5和6个字符,但撤消操作不起作用(文本不变(,尽管Edit_CanUndo
返回1
。如果我关闭了updateHighlighting
,那么撤消就可以了,但它一次回滚太多(例如,输入一个长文本,按Ctrl+V粘贴一个文本,输入另一个文本-只需要撤消3次就可以全部回滚(。
我的代码出了什么问题?也许我应该从updateHighlighting
返回一个特定的值?
#define UNICODE
#define _UNICODE
#include <tchar.h>
#include <windows.h>
#include <windowsx.h>
#include <commctrl.h>
#include <richedit.h>
#define IDC_EDITOR 1000
#define IDC_BUTTON 1001
HWND hEditorWnd;
bool updateHighlighting(HWND hWnd) {
int carriagePos = 0;
SendMessage(hWnd, EM_GETSEL, (WPARAM)&carriagePos, (WPARAM)&carriagePos);
CHARFORMAT cf = {0};
cf.cbSize = sizeof(CHARFORMAT) ;
cf.dwMask = CFM_COLOR | CFM_BOLD;
cf.crTextColor = RGB(0, 0, 200);
cf.dwEffects = CFM_BOLD;
SendMessage(hWnd, EM_SETSEL, 0, -1);
SendMessage(hWnd, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf);
cf.dwMask = CFM_COLOR | CFM_BOLD;
cf.dwEffects = cf.dwEffects & ~CFM_BOLD;
cf.crTextColor = RGB(200, 0, 0);
SendMessage(hWnd, EM_SETSEL, 4, 6);
SendMessage(hWnd, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf);
SendMessage(hWnd, EM_SETSEL, carriagePos, carriagePos);
_tprintf(TEXT("End highlightn"));
return true;
}
LRESULT CALLBACK WindowProcedure (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
case WM_DESTROY:
PostQuitMessage (0);
break;
case WM_COMMAND: {
if (LOWORD(wParam) == IDC_EDITOR && HIWORD(wParam) == EN_CHANGE)
return updateHighlighting(hEditorWnd);
if (LOWORD(wParam) == IDC_BUTTON && HIWORD(wParam) == BN_CLICKED)
_tprintf(TEXT("UNDO: can %i => result: %in"), Edit_CanUndo(hEditorWnd), Edit_Undo(hEditorWnd));
}
break;
default:
return DefWindowProc (hWnd, msg, wParam, lParam);
}
return 0;
}
int WINAPI WinMain (HINSTANCE hThisInstance, HINSTANCE hPrevInstance, LPSTR lpszArgument, int nCmdShow) {
InitCommonControls();
LoadLibrary(TEXT("msftedit.dll"));
HWND hWnd;
MSG msg;
WNDCLASSEX wincl;
wincl.hInstance = hThisInstance;
wincl.lpszClassName = TEXT("AppClass");
wincl.lpfnWndProc = WindowProcedure;
wincl.style = CS_DBLCLKS;
wincl.cbSize = sizeof (WNDCLASSEX);
wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION);
wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
wincl.lpszMenuName = NULL;
wincl.cbClsExtra = 0;
wincl.cbWndExtra = 0;
wincl.hbrBackground = (HBRUSH) COLOR_BACKGROUND;
if (!RegisterClassEx (&wincl))
return 0;
hWnd = CreateWindowEx (0, TEXT("AppClass"), TEXT("Test"), WS_OVERLAPPEDWINDOW | WS_VISIBLE, 300, 400, 310, 375, HWND_DESKTOP, NULL, hThisInstance, NULL);
CreateWindowEx(0, WC_BUTTON , L"UNDO", WS_VISIBLE | WS_CHILD | WS_TABSTOP, 10, 310, 280, 30, hWnd, (HMENU)IDC_BUTTON, hThisInstance, NULL);
hEditorWnd = CreateWindowEx(0, TEXT("RICHEDIT50W"), NULL, WS_VISIBLE | WS_CHILD | ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN | WS_VSCROLL | WS_HSCROLL | WS_TABSTOP | ES_NOHIDESEL, 0, 0, 300, 300, hWnd, (HMENU)IDC_EDITOR, hThisInstance, NULL);
SendMessage(hEditorWnd, EM_SETEVENTMASK, 0, ENM_CHANGE | ENM_UPDATE | ENM_KEYEVENTS);
while (GetMessage (&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
附言:我在Win7x64上使用代码::块17.12+gcc5.1(但构建一个32位应用程序(。
您对"不起作用";或者当它";确实有效";不清楚,然而,改变颜色也是可以撤消的,所以如果你不想这样做,那么你必须在着色前关闭撤消,在着色后恢复。
我不知道是否有更简单的方法,但在C中,我使用以下函数:
//#include <windows, richedit, ...>
#include <TOM.h>
extern HWND ghEditWnd;
static IUnknown *pUnk;
static ITextDocument *pDoc;
static const IID IID_ITextDocument = {
0x8CC497C0, 0xA1DF, 0x11CE,
{0x80,0x98,0x00,0xAA,0x00,0x47,0xBE,0x5D}
};
static int tomInit(void)
{
if (!pUnk) SendMessage(ghEditWnd, EM_GETOLEINTERFACE, 0, (LPARAM)&pUnk);
if (!pUnk || pUnk->lpVtbl->QueryInterface(pUnk,&IID_ITextDocument, &pDoc) != NOERROR)
{pUnk= 0; return FALSE;}
return TRUE;
}
void undoSuspend(void)
{
if (!pUnk) if (!tomInit()) return;
pDoc->lpVtbl->Undo(pDoc,tomSuspend,0); //Suspends Undo.
}
void undoResume(void)
{
if (!pUnk) if (!tomInit()) return;
pDoc->lpVtbl->Undo(pDoc,tomResume,0); // resume Undo.
}
void releaseTOM(void)
{
if (pUnk) {
pUnk->lpVtbl->Release(pUnk); pUnk= 0; pDoc= 0;
}
}
另请参阅https://learn.microsoft.com/en-us/windows/win32/api/tom/
编辑:有问题如何在WPF的富文本框中禁用撤消?建议将UndoLimit设置为零。目前尚不清楚这是否也会清除现有的撤消堆栈。
微软表示,在RichEdit 3.0中,撤消可以暂停并恢复。
这是一个基于Paul Ogilvie的回复和codepilot答案的工作示例(undo根据我的意愿逐字符删除符号(。
提示:使用freeze/unfreeze
来防止闪烁。这类似于SetWindowRedraw(hWnd, TRUE/FALSE)
,但是最后一个操作引起不必要的EN_SELCHANGE
通知消息
#define UNICODE
#define _UNICODE
#include <tchar.h>
#include <windows.h>
#include <windowsx.h>
#include <commctrl.h>
#include <richedit.h>
// Code::Block doesn't have a tom.h :(
// https://github.com/kinke/mingw-w64-crt/blob/master/mingw-w64-headers/include/tom.h
#include "tom.h"
#include <richole.h>
#include <unknwn.h>
IUnknown *tr_code = NULL;
ITextDocument *td_code;
#define DEFINE_GUIDXXX(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) EXTERN_C const GUID name = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } }
DEFINE_GUIDXXX(IID_ITextDocument,0x8CC497C0,0xA1DF,0x11CE,0x80,0x98, 0x00,0xAA,0x00,0x47,0xBE,0x5D);
#define IDC_EDITOR 1000
#define IDC_BUTTON 1001
HWND hEditorWnd;
bool updateHighlighting(HWND hWnd) {
int carriagePos = 0;
SendMessage(hWnd, EM_GETSEL, (WPARAM)&carriagePos, (WPARAM)&carriagePos);
CHARFORMAT cf = {0};
cf.cbSize = sizeof(CHARFORMAT) ;
cf.dwMask = CFM_COLOR | CFM_BOLD;
cf.crTextColor = RGB(0, 0, 200);
cf.dwEffects = CFM_BOLD;
SendMessage(hWnd, EM_SETSEL, 0, -1);
SendMessage(hWnd, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf);
cf.dwMask = CFM_COLOR | CFM_BOLD;
cf.dwEffects = cf.dwEffects & ~CFM_BOLD;
cf.crTextColor = RGB(200, 0, 0);
SendMessage(hWnd, EM_SETSEL, 4, 6);
SendMessage(hWnd, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf);
SendMessage(hWnd, EM_SETSEL, carriagePos, carriagePos);
_tprintf(TEXT("End highlightn"));
return true;
}
LRESULT CALLBACK WindowProcedure (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
case WM_DESTROY:
PostQuitMessage (0);
break;
case WM_COMMAND: {
if (LOWORD(wParam) == IDC_BUTTON && HIWORD(wParam) == BN_CLICKED)
_tprintf(TEXT("UNDO: can %i => result: %in"), Edit_CanUndo(hEditorWnd), Edit_Undo(hEditorWnd));
if (LOWORD(wParam) == IDC_EDITOR && HIWORD(wParam) == EN_CHANGE) {
long cnt = 0;
td_code->Undo(tomSuspend, NULL); // <---
td_code->Freeze(&cnt); // <---
updateHighlighting(hEditorWnd);
td_code->Unfreeze(&cnt); // <---
td_code->Undo(tomResume, NULL); // <---
return 1;
}
}
break;
default:
return DefWindowProc (hWnd, msg, wParam, lParam);
}
return 0;
}
int WINAPI WinMain (HINSTANCE hThisInstance, HINSTANCE hPrevInstance, LPSTR lpszArgument, int nCmdShow) {
InitCommonControls();
LoadLibrary(TEXT("msftedit.dll"));
HWND hWnd;
MSG msg;
WNDCLASSEX wincl;
wincl.hInstance = hThisInstance;
wincl.lpszClassName = TEXT("AppClass");
wincl.lpfnWndProc = WindowProcedure;
wincl.style = CS_DBLCLKS;
wincl.cbSize = sizeof (WNDCLASSEX);
wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION);
wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
wincl.lpszMenuName = NULL;
wincl.cbClsExtra = 0;
wincl.cbWndExtra = 0;
wincl.hbrBackground = (HBRUSH) COLOR_BACKGROUND;
if (!RegisterClassEx (&wincl))
return 0;
hWnd = CreateWindowEx (0, TEXT("AppClass"), TEXT("Test"), WS_OVERLAPPEDWINDOW | WS_VISIBLE, 300, 400, 310, 375, HWND_DESKTOP, NULL, hThisInstance, NULL);
CreateWindowEx(0, WC_BUTTON , L"UNDO", WS_VISIBLE | WS_CHILD | WS_TABSTOP, 10, 310, 280, 30, hWnd, (HMENU)IDC_BUTTON, hThisInstance, NULL);
hEditorWnd = CreateWindowEx(0, TEXT("RICHEDIT50W"), NULL, WS_VISIBLE | WS_CHILD | ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN | WS_VSCROLL | WS_HSCROLL | WS_TABSTOP | ES_NOHIDESEL, 0, 0, 300, 300, hWnd, (HMENU)IDC_EDITOR, hThisInstance, NULL);
SendMessage(hEditorWnd, EM_SETEVENTMASK, 0, ENM_CHANGE | ENM_UPDATE | ENM_KEYEVENTS);
// /
SendMessage(hEditorWnd, EM_GETOLEINTERFACE, 0, (LPARAM)&tr_code);
if(tr_code == (IRichEditOle*)NULL) {
MessageBox(0, TEXT("Error when trying to get RichEdit OLE Object"), NULL, 0);
return 0;
}
tr_code->QueryInterface(IID_ITextDocument,(void**)&td_code);
// /
while (GetMessage (&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}