Windows 标准控件 ComboBox 的改造
目的
windows 下拉列表的标准控件为ComboBox(WC_COMBOBOX),对复选模式、只读模式支持不太好,该内容尝试对其进行复选模式、只读模式改造。
代码
以下为测试代码,封装并不完整(主要为父窗口的Notify类消息,需要设计框架),但已经基本实现只读、复选功能。
/*
ComboBox的简单改造:
1、改造主要依托 CBS_DROPDOWN(含一个listbox,一个Edit),
主要是这种样式的控件有个好处,展示内容不但可见,还可以复制文本
2、支持单选/多选模式,只读模式,目前多选模式只允许选择,不允许输入,以便控制输入有效性。
关于只读模式:
对于单选模式,预先存储设置只读时选中的itemindex,同时处理ListBox的CBN_SELCHANGE 和 内嵌 Edit的 EN_CHANGE,
无论如何变动,设置ComboBox的CurSelIndex为之前存储的值
对于复选模式,需要处理内嵌Edit的值(依据ItemData获取),同时对于ListBox的窗口过程改写,ItemData的check状态不允许修改。
3、原生CBS_DROPDOWN单选模式,支持输入自动填充(Auto Complete)(简单改造WM_COMMAND/EN_CHANGE).
4、禁用Edit的右键菜单.
5、自带Label.
6、基于comctrl 6.0
注意点:
1、ComboBox_InsertItemData 与 ComboBox_SetItemData 均需要调用,前者获得插入索引,后者挂钩itemdata.
进一步:
如果试图将数据源与之关联,必须实现类似virtual style.
对于一次加载的数据,目前已经能支持,ComboBox_AddItem对分配做预规划
对于动态调整的数据,则需要频繁调用ComboBox_SetItemData 调整与 内存块的映射。
*/
#include<windows.h>
#include<windowsx.h>
#include<math.h>
#include<stdio.h>
#include<commctrl.h>
#include<Richedit.h>
#include<gdiplus.h>
using namespace Gdiplus;
#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
#pragma comment(lib,"user32.lib")
#pragma comment(lib,"gdi32.lib")
#pragma comment(lib,"kernel32.lib")
#pragma comment(lib,"comctl32.lib")
#pragma comment(lib,"gdiplus.lib")
#define IDC_COMBOBOX_01 0x0001
#define IDC_COMBOBOX_02 0x0002
#define IDC_COMBOBOX_03 0x0003
#define IDC_COMBOBOX_04 0x0004
#define WINDOW_CLASS_NAME "CheckComboBox"
#define RWIDTH(A) abs(A.right - A.left)
#define RHEIGHT(A) abs(A.bottom - A.top)
HINSTANCE instance;
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
LRESULT CALLBACK WindowProc(HWND, UINT, WPARAM, LPARAM);
int ComboBoxCtrl_Test(HWND parent);
int ComboBox_AddData_Test(HWND hwnd);
typedef struct _STRUCT_COMBOBOXITEM_ {
char text[256];
BOOL checked;
void* value;
}RComboBoxItem,*pComboBoxItem;
typedef struct _STRUCT_COMBOBOX_STYLE_ {
//窗口样式及资源信息
HFONT font;
HFONT font_title;
COLORREF color_title;
COLORREF color_text;
COLORREF color_text_readonly;
COLORREF color_bk;
COLORREF color_border;
COLORREF color_readonly;
HBRUSH brush;
HBRUSH brush_border;
HBRUSH brush_readonly;
HPEN pen;
int margin_left;//标签宽度
int font_cy;//字体高度
WNDPROC proc; //窗口过程
WNDPROC pre_proc; //原窗口过程
char title[256]; //标签
BOOL is_multicheck;//复选标记
BOOL alter_allowed;//是否允许修改,通过ComboBox_SetReadOnly维护
char* plt;//复选模式下的选中文本缓存
int cur_index;//单选模式下只读前存储的选中索引
}RComboBoxStyle,*pComboBoxStyle;
int ComboBox_InitialSettings(HWND hwnd,char* title);
pComboBoxStyle ComboBox_GetSettings(HWND hwnd);
int ComboBox_AddItem(HWND hwnd,char* text,BOOL checked);
int ComboBox_ClearSettings(HWND hwnd);
int ComboBox_NCCalcSize(HWND hwnd,LPNCCALCSIZE_PARAMS calc);
int ComboBox_NCPaint(HWND hwnd);
int ComboBox_SetTitleOffset(HWND hwnd,int margin_left);
LRESULT CALLBACK ComboBoxOwnerProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
int ComboBox_GetMultiSelectText(HWND hwnd,char* plt);
int ComboBox_SingleLineComplete(HWND hwnd,HWND ctrl_edit);//自动补全
int ComboBox_SetReadOnly(HWND hwnd,BOOL is_readonly);
typedef struct _STRUCT_CHECKCOMBOBOX_PARAM_ {
WNDPROC proc;
WNDPROC pre_proc;
HWND combobox;
HWND edit;
} RLBParam,*pLBParam;
int ComboLBox_InitialSettings(HWND combobox,char* ctrl_classname);
pLBParam ComboLBox_GetSettings(HWND ctrl);
int ComboLBox_ClearSettings(HWND ctrl);
LRESULT CALLBACK ComboLBoxOwnerProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK ComboTextOwnerProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain(HINSTANCE hinstance, HINSTANCE hprevinstance, PSTR szcmdLine, int icmdshow)
{
HWND hwnd;
MSG msg;
WNDCLASSEX winclass;
InitCommonControls();
instance = hinstance;
winclass.cbSize = sizeof(WNDCLASSEX);
winclass.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
winclass.lpfnWndProc = WindowProc;
winclass.cbClsExtra = 0;
winclass.cbWndExtra = 0;
winclass.hInstance = hinstance;
winclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
winclass.hCursor = LoadCursor(NULL, IDC_ARROW);
winclass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
winclass.lpszMenuName = NULL;
winclass.lpszClassName = WINDOW_CLASS_NAME;
winclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
if (!RegisterClassEx(&winclass)) return 0;
if (!(hwnd = CreateWindowEx(NULL,
WINDOW_CLASS_NAME,
"ComboBox test",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
240, 262,
780,400,
NULL,
NULL,
hinstance,
NULL)))
return 0;
while (GetMessage(&msg, NULL, 0, 0)) {
if(!IsDialogMessage(hwnd,&msg)) { //保证tabstop消息正常发送
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return(msg.wParam);
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
switch (msg)
{
case WM_CREATE: {
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
InitCommonControls();
ComboBoxCtrl_Test(hwnd);
} break;
case WM_SIZE: {
int height = HIWORD(lparam);
int width = LOWORD(lparam);
//设置Z序
//SetWindowPos(GetDlgItem(hwnd, IDC_COMBOBOX_01),HWND_BOTTOM,5,5,240,25,SWP_SHOWWINDOW);
//SetWindowPos(GetDlgItem(hwnd, IDC_COMBOBOX_02),HWND_BOTTOM,5,35,240,25,SWP_SHOWWINDOW);
//SetWindowPos(GetDlgItem(hwnd, IDC_COMBOBOX_03),HWND_BOTTOM,5,65,240,25,SWP_SHOWWINDOW);
}break;
case WM_DESTROY: {
GdiplusShutdown(gdiplusToken);
PostQuitMessage(0);
return (0);
} break;
case WM_COMMAND: {
HWND ctrl=(HWND)lparam;
UINT code=HIWORD(wparam);
if(code==CBN_SELCHANGE ) {
pComboBoxStyle cs=ComboBox_GetSettings(ctrl);
if((!cs)||(cs->alter_allowed)||(cs->is_multicheck)) break;
//单选只读,此处禁用listbox选择
ComboBox_SetCurSel(ctrl,cs->cur_index);
return 0;
}
} break;
case WM_NOTIFY: {
} break;
case WM_DRAWITEM: {
UINT ctrl_id=(UINT)wparam;
LPDRAWITEMSTRUCT pDraw=(LPDRAWITEMSTRUCT)lparam;
if(ODT_COMBOBOX==pDraw->CtlType) {
HDC hdc=pDraw->hDC;
RECT rc={0};
UINT itemid=pDraw->itemID;
HWND ctrl=pDraw->hwndItem;
pComboBoxItem pdata=(pComboBoxItem)pDraw->itemData;
pComboBoxStyle cs=ComboBox_GetSettings(ctrl);
if((!pdata)||(!cs)||itemid<0) {
return FALSE;
}
if((GetWindowLongPtr(pDraw->hwndItem,GWL_STYLE)&CBS_DROPDOWNLIST)==CBS_DROPDOWNLIST) {//只读,没有Edit
return FALSE;
}
else CopyRect(&rc,&pDraw->rcItem);
//绘制item.
SelectObject(hdc,cs->pen);
SelectObject(hdc,cs->font);
COLORREF color_text=cs->color_text;
COLORREF color_text_check=RGB(250,250,250);
COLORREF color_check=RGB(0,120,250);
HBRUSH brush=cs->alter_allowed?cs->brush:cs->brush_readonly;
HBRUSH brush_check=CreateSolidBrush(color_check);
HBRUSH brush_border=CreateSolidBrush(cs->color_border);
RECT rc_check={0};
CopyRect(&rc_check,&rc);
rc_check.right=rc_check.left+(rc.bottom-rc.top);
if(pDraw->itemState==ODS_SELECTED) {
FillRect(hdc,&rc,brush_check);
SetBkMode(hdc,TRANSPARENT);
SetTextColor(hdc,color_text_check);
rc.left=rc_check.right;
if(pdata->text) DrawText(hdc,pdata->text,-1,&rc,DT_LEFT|DT_VCENTER);
InflateRect(&a