问题
I finally managed to get my syntax highlighting done using richedit and iczelion's tutorials. Now that i find it, it certainly is not fast enough. I am thinking of taking this one step ahead: a custom edit control. But i do not know how to go about it. Could you guys tell me how to go about it? Give me some info to start on? Maybe even some tutorial or suggest some book?
Now I'm not asking for you guys to spell it out for me, just something to start on. I will be using C++/ASM/Win32 API for this. I'm sure many of you have already made custom edit controls befores, so may be you could even share your experience.
Thanks,
Devjeet
回答1:
You are going to want to look into Owner Drawn Controls. MSDN has an example of doing this with a list box. You can adapt this to an Edit control.
回答2:
I spent one day on coding my own custom edit control - it's working well, so I would like to share my experience here, maybe for someone this code might be helpful... Because custom draw an common edit control is impossible (see here), you must write your own edit control. The general steps are:
// global vars
int select; // current selection position
int cursor; // current cursor position
HWND parent; // parent window
wchar_t buf[MAXINPUTBUF]; // edit buffer
WNDPROC oldproc; // old window procedure
// create custom control window
hWnd = CreateWindowW(L"static", NULL, WS_CHILD | WS_TABSTOP | SS_LEFT | SS_NOTIFY, 0, 0, 0, 0, parent, NULL, (HINSTANCE)GetWindowLongPtr(parent, GWL_HINSTANCE), NULL);
// todo: use SetProp() to store all global vars
oldproc = (WNDPROC)SetWindowLongPtrW(hWnd, GWL_WNDPROC, (LONG_PTR)InputWndProc);
SetWindowPos(hWnd, HWND_TOP, x, y, cx, cy, 0);
How to display keyboard input is described here. My window procedure looks as follows
LRESULT CALLBACK InputWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_LBUTTONDOWN:
//SetFocus(hWnd);
PostMessageW(GetParent(hWnd), WM_NEXTDLGCTL, (WPARAM)hWnd, TRUE);
break;
case WM_KILLFOCUS:
HideCaret(hWnd);
DestroyCaret();
break;
case WM_SETFOCUS:
{
RECT r;
GetClientRect(hWnd, &r);
// Create a solid black caret.
CreateCaret(hWnd, (HBITMAP) NULL, 2, r.bottom-r.top);
ShowCaret(hWnd);
InputWndRedraw(hWnd);
}
return FALSE;
case WM_GETDLGCODE:
return DLGC_WANTALLKEYS | DLGC_WANTARROWS;
case WM_KEYDOWN:
{
switch (wParam)
{
case 'V':
if (0x8000 & GetKeyState(VK_CONTROL))
{
HANDLE h;
wchar_t *cb;
int len,slen;
InputWndDelete(hWnd);
OpenClipboard(NULL);
h = GetClipboardData(CF_UNICODETEXT);
cb = (wchar_t*)GlobalLock(h);
if (cb)
{
memcpy(buf+(cursor+len)*sizeof(wchar_t), buf+cursor*sizeof(wchar_t), (slen-cursor)*sizeof(wchar_t));
memcpy(buf+cursor*sizeof(wchar_t), cb, len*sizeof(wchar_t));
}
GlobalUnlock(h);
CloseClipboard();
InputWndRedraw(hWnd);
}
break;
case VK_RIGHT:
if (cursor-1 >= MAXINPUTBUF || cursor >= (int)wcslen(buf))
break;
cursor++;
if (!(GetKeyState(VK_SHIFT) & 0x8000))
select = cursor;
InputWndRedraw(hWnd);
break;
case VK_TAB:
PostMessageW(GetParent(hWnd), WM_NEXTDLGCTL, GetKeyState(VK_SHIFT) & 0x8000, FALSE);
break;
case VK_LEFT:
if (cursor <= 0)
break;
cursor--;
if (!(GetKeyState(VK_SHIFT) & 0x8000))
select = cursor;
InputWndRedraw(hWnd);
break;
case VK_HOME:
cursor = 0;
if (!(GetKeyState(VK_SHIFT) & 0x8000))
select = cursor;
InputWndRedraw(hWnd);
break;
case VK_END:
cursor = wcslen(buf);
if (!(GetKeyState(VK_SHIFT) & 0x8000))
select = cursor;
InputWndRedraw(hWnd);
break;
case VK_DELETE:
if (cursor >= (int)wcslen(buf))
{
InputWndDelete(hWnd);
InputWndRedraw(hWnd);
break;
}
if (select == cursor)
select ++;
InputWndDelete(hWnd);
InputWndRedraw(hWnd);
break;
case VK_BACK:
if (cursor <= 0)
{
InputWndDelete(hWnd);
InputWndRedraw(hWnd);
break;
}
if (select == cursor)
cursor --;
InputWndDelete(hWnd);
InputWndRedraw(hWnd);
}
}
break;
case WM_CHAR:
if (wParam < VK_SPACE)
break;
InputWndDelete(hWnd);
if (wcslen(buf)+1 < MAXINPUTBUF)
{
wmemmove(buf+(cursor+1)*sizeof(wchar_t), buf+cursor*sizeof(wchar_t), wcslen(s->buf)-cursor);
buf[cursor] = wParam;
cursor++;
select = cursor;
}
InputWndRedraw(hWnd);
break;
case WM_ERASEBKGND:
// no flickering
return TRUE;
case WM_PAINT:
{
HDC dc;
PAINTSTRUCT paint;
dc = BeginPaint(hWnd, &paint);
InputWndDraw(hWnd, dc);
EndPaint(hWnd, &paint);
}
return TRUE;
}
return CallWindowProcW(oldproc, hWnd, msg, wParam, lParam);
}
Delete current selected text (from select to cursor).
void InputWndDelete(HWND hWnd)
{
int len;
len = wcslen(buf);
if (select > cursor)
{
memcpy(buf+cursor*sizeof(wchar_t), buf+select*sizeof(wchar_t), (len - select)*sizeof(wchar_t));
ZeroMemory(buf+(len-select+cursor)*sizeof(wchar_t), (MAXINPUTBUF-len+select-cursor)*sizeof(wchar_t));
select = cursor;
}
else if (select < cursor)
{
memcpy(buf+select*sizeof(wchar_t), buf+cursor*sizeof(wchar_t), (len - cursor)*sizeof(wchar_t));
ZeroMemory(buf+(len-cursor+select)*sizeof(wchar_t), (MAXINPUTBUF-len+cursor-select)*sizeof(wchar_t));
cursor = select;
}
else
{
select = cursor;
}
}
Draw window on window DC
void InputWndRedraw(HWND hWnd)
{
HDC hdc;
HideCaret(hWnd);
hdc = GetDC(hWnd);
InputWndDraw(hWnd, hdc);
ReleaseDC(hWnd, hdc);
ShowCaret(hWnd);
}
Draw input buffer (buf*) on device context. Syntax highlighting and other formatting features goes here...
void InputWndDraw(HWND hWnd, HDC hdc)
{
RECT r,cr;
GetClientRect(hWnd, &cr);
// draw selected rectangle FillRect()...
CopyRect(&r,&cr);
DrawTextW(hdc, buf, -1, &r, DT_LEFT | DT_TOP);
if (cursor)
DrawTextW(hdc, buf, cursor, &r, DT_LEFT | DT_TOP | DT_CALCRECT);
else
r.right = cr.left;
if (GetFocus() == hWnd)
{
if (r.right > cr.right)
SetCaretPos(cr.right, cr.top);
else
SetCaretPos(r.right, cr.top);
}
}
来源:https://stackoverflow.com/questions/8074047/custom-edit-control-win32