Windows 标准控件 ComboBox 的改造

本文介绍了如何改造Windows标准控件ComboBox,以支持复选模式和只读模式。通过提供的测试代码,展示了在不同模式下ComboBox的功能实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值