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) {//只读,没有Editreturn 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(&rc_check,-3,-3);FrameRect(hdc,&rc_check,brush_border);if(pdata->checked) DrawText(hdc,"√",-1,&rc_check,DT_CENTER|DT_VCENTER);}else {FillRect(hdc,&rc,brush);SetBkMode(hdc,TRANSPARENT);SetTextColor(hdc,cs->alter_allowed?cs->color_text:cs->color_text_readonly);rc.left=rc_check.right;if(pdata->text) DrawText(hdc,pdata->text,-1,&rc,DT_LEFT|DT_VCENTER);InflateRect(&rc_check,-3,-3);FrameRect(hdc,&rc_check,brush_border);if(pdata->checked) DrawText(hdc,"√",-1,&rc_check,DT_CENTER|DT_VCENTER);}DeleteObject(brush_check);DeleteObject(brush_border);return TRUE;}} break;case WM_CTLCOLORLISTBOX:{HDC dc=(HDC)wparam;HWND ctrl=(HWND)lparam;SetBkMode(dc,TRANSPARENT);pComboBoxStyle cs=ComboBox_GetSettings(ctrl);if(cs) {SetTextColor(dc,cs->alter_allowed?cs->color_text:cs->color_text_readonly);SetBkColor(dc,cs->color_bk);return (LRESULT)CreateSolidBrush(cs->alter_allowed?cs->color_bk:cs->color_readonly);//cs->brush;}else {SetTextColor(dc,RGB(220,220,220));SetBkColor(dc,RGB(70,70,70));return (LRESULT)CreateSolidBrush(RGB(30,30,30));//cs->brush;}} break;case WM_CTLCOLOREDIT:{HDC dc=(HDC)wparam;HWND ctrl=(HWND)lparam;SetBkMode(dc,TRANSPARENT);pComboBoxStyle cs=ComboBox_GetSettings(ctrl);if(cs) {SetTextColor(dc,cs->color_text);SetBkColor(dc,cs->color_bk);return (LRESULT)CreateSolidBrush(cs->color_bk);//cs->brush;}else {SetTextColor(dc,RGB(200,200,200));SetBkColor(dc,RGB(70,70,70));return (LRESULT)CreateSolidBrush(RGB(30,30,30));//cs->brush;}} break;default:break;}return DefWindowProc(hwnd, msg, wparam, lparam);
}int ComboLBox_InitialSettings(HWND combobox,char* ctrl_name)
{COMBOBOXINFO cbi={0};cbi.cbSize=sizeof(cbi);GetComboBoxInfo(combobox,&cbi);HWND lb;pLBParam pls=(pLBParam)calloc(sizeof(RLBParam),1);if(!pls) return -1;if(strstr(ctrl_name,WC_EDIT)) {lb=cbi.hwndItem;pls->proc=(WNDPROC)ComboTextOwnerProc;pls->pre_proc=(WNDPROC)SetWindowLongPtr(lb,GWLP_WNDPROC,(LONG_PTR)pls->proc);pls->combobox=combobox;}else {lb=cbi.hwndList;pls->proc=(WNDPROC)ComboLBoxOwnerProc;pls->pre_proc=(WNDPROC)SetWindowLongPtr(lb,GWLP_WNDPROC,(LONG_PTR)pls->proc);pls->combobox=combobox;}SetWindowLongPtr(lb,GWLP_USERDATA,(LONG_PTR)pls);return 0;
}pLBParam ComboLBox_GetSettings(HWND listbox)
{return (pLBParam)GetWindowLongPtr(listbox,GWLP_USERDATA);
}int ComboLBox_ClearSettings(HWND ctrl)
{pLBParam pls=(pLBParam)ComboLBox_GetSettings(ctrl);if(!pls) return 0;SetWindowLongPtr(ctrl,GWLP_WNDPROC,(LONG_PTR)pls->pre_proc);free(pls);return 0;
}//checkComboBox,需要改写listBox的窗口函数
LRESULT CALLBACK ComboLBoxOwnerProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{pLBParam pls=ComboLBox_GetSettings(hwnd);if(!pls) return 0;WNDPROC pre_proc=pls->pre_proc;switch(msg) {case WM_CHAR: {//空格管理选中/取消if(wParam==VK_SPACE) {}} break;case WM_LBUTTONDOWN: {//设置选择POINT pt={GET_X_LPARAM(lParam),GET_Y_LPARAM(lParam)};RECT rc={0};GetClientRect(hwnd,&rc);if(PtInRect(&rc,pt)) {//获取点击的item index;int item_height=ListBox_GetItemHeight(hwnd,0);int top_index=ListBox_GetTopIndex(hwnd);int hit_index=top_index+(pt.y/item_height);RECT rc_item={0};ListBox_GetItemRect(hwnd,hit_index,&rc_item);if(PtInRect(&rc_item,pt)) {//设置选中或者取消选中。pComboBoxItem data=(pComboBoxItem)ComboBox_GetItemData(pls->combobox,hit_index);pComboBoxStyle cs=ComboBox_GetSettings(pls->combobox);if(!cs->alter_allowed) break;//不支持修改模式data->checked=!(data->checked);InvalidateRect(hwnd,NULL,TRUE);if((GetWindowLongPtr(pls->combobox,GWL_STYLE)&CBS_DROPDOWN)==CBS_DROPDOWN) {COMBOBOXINFO cbi={0};cbi.cbSize=sizeof(cbi);GetComboBoxInfo(pls->combobox,&cbi);char* plt=cs->plt;ComboBox_GetMultiSelectText(pls->combobox,plt); Edit_SetText(cbi.hwndItem,plt);}else InvalidateRect(pls->combobox,NULL,TRUE);}}} break;case WM_LBUTTONUP: {return 0;} break;case WM_NCDESTROY: {ComboLBox_ClearSettings(hwnd);}}return CallWindowProc(pre_proc,hwnd,msg,wParam,lParam);
}LRESULT CALLBACK ComboBoxOwnerProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{pComboBoxStyle cs=ComboBox_GetSettings(hwnd);switch(msg) { case WM_CTLCOLOREDIT:{HDC dc=(HDC)wParam;HWND ctrl=(HWND)lParam;SetBkMode(dc,TRANSPARENT);pComboBoxStyle cs=ComboBox_GetSettings(hwnd);if(cs) {SetTextColor(dc,cs->alter_allowed?cs->color_text:cs->color_text_readonly);SetBkColor(dc,cs->alter_allowed?cs->color_bk:cs->color_readonly); //0x383838return (LRESULT)CreateSolidBrush(cs->alter_allowed?cs->color_bk:cs->color_readonly);//cs->brush;}else {SetTextColor(dc,RGB(200,200,200));SetBkColor(dc,RGB(70,70,70)); //0x383838return (LRESULT)CreateSolidBrush(RGB(30,30,30));//cs->brush;}} break;case WM_CTLCOLORSTATIC:{HDC dc=(HDC)wParam;HWND ctrl=(HWND)lParam;SetBkMode(dc,TRANSPARENT);pComboBoxStyle cs=ComboBox_GetSettings(hwnd);if(cs) {SetTextColor(dc,cs->alter_allowed?cs->color_text:cs->color_text_readonly);SetBkColor(dc,cs->color_bk); //0x383838return (LRESULT)CreateSolidBrush(cs->alter_allowed?cs->color_bk:cs->color_readonly);//cs->brush;}else {SetTextColor(dc,RGB(200,200,200));SetBkColor(dc,RGB(70,70,70)); //0x383838return (LRESULT)CreateSolidBrush(RGB(30,30,30));//cs->brush;}} break;case WM_COMMAND: {HWND ctrl=(HWND)lParam;UINT code=HIWORD(wParam);if(code==EN_CHANGE) {if(cs&&cs->is_multicheck) {char* plt=cs->plt;ComboBox_GetMultiSelectText(hwnd,plt);Edit_SetText(ctrl,plt);return 0;}else if(cs&&(!cs->is_multicheck)) {if(!cs->alter_allowed) {//单选只读,此处禁用文本输入if(-1!=cs->cur_index) {ComboBox_SetCurSel(hwnd,cs->cur_index);pComboBoxItem pdata=(pComboBoxItem)ComboBox_GetItemData(hwnd,cs->cur_index);if(!pdata) return 0;Edit_SetText(ctrl,pdata->text);}else Edit_SetText(ctrl,"");return 0;} else if(0==ComboBox_SingleLineComplete(hwnd,ctrl)) return 0;}}} break;case WM_NCCALCSIZE: {if(wParam==TRUE) {LPNCCALCSIZE_PARAMS calc_param = (LPNCCALCSIZE_PARAMS)lParam;return ComboBox_NCCalcSize(hwnd,calc_param);}} break;case CB_GETLBTEXT: {//多选模式,索引为-1,取出所有选中文本值if(cs->is_multicheck&&-1==wParam) {char* plt=(char*)lParam;int length=ComboBox_GetMultiSelectText(hwnd,plt);return length;}} break;case WM_LBUTTONDOWN: {POINT hit={GET_X_LPARAM(lParam),GET_Y_LPARAM(lParam)};COMBOBOXINFO cbi={0};cbi.cbSize=sizeof(cbi);GetComboBoxInfo(hwnd,&cbi);RECT btn_rc={0},client_rc={0};GetClientRect(hwnd,&client_rc);btn_rc.top=cbi.rcButton.top;btn_rc.bottom=cbi.rcButton.bottom;btn_rc.left=client_rc.right-(cbi.rcButton.right-cbi.rcButton.left);btn_rc.right=client_rc.right;if(PtInRect(&btn_rc,hit)) {SetFocus(hwnd);BOOL isdrop_visible=GetWindowLongPtr(cbi.hwndList,GWL_STYLE)&WS_VISIBLE;if(!isdrop_visible) {ComboBox_ShowDropdown(hwnd,TRUE);}else {ComboBox_ShowDropdown(hwnd,FALSE);}return 0;}} break;case WM_PAINT: {//绘制操作PAINTSTRUCT ps;COMBOBOXINFO cbi={0};cbi.cbSize=sizeof(cbi);GetComboBoxInfo(hwnd,&cbi);HDC hdc=BeginPaint(hwnd,&ps);SelectObject(hdc,cs->pen);SelectObject(hdc,cs->alter_allowed?cs->brush:cs->brush_readonly);Rectangle(hdc,ps.rcPaint.left,ps.rcPaint.top,ps.rcPaint.right,ps.rcPaint.bottom);SetTextColor(hdc,cs->color_border);SetBkMode(hdc,TRANSPARENT);SelectObject(hdc,cs->font);OffsetRect(&cbi.rcButton,-cs->margin_left,0);DrawText(hdc,"∨",-1,&cbi.rcButton,DT_CENTER|DT_VCENTER);char text[1024]="";//对于CBS_DROPDOWNLIST样式的combobox,没有内嵌的Edit,其文本需要手工绘制,包括CueBanner.//已弃用if((GetWindowLongPtr(hwnd,GWL_STYLE)&CBS_DROPDOWNLIST)==CBS_DROPDOWNLIST) {if(cs->is_multicheck) {//获取文本.int length=ComboBox_GetMultiSelectText(hwnd,cs->plt);SetTextColor(hdc,cs->color_text);if(length<=0&&hwnd!=GetFocus()) {wchar_t wc_banner[256];ComboBox_GetCueBannerText(hwnd,wc_banner,sizeof(wc_banner));int len=WideCharToMultiByte(CP_ACP,0,wc_banner,wcslen(wc_banner),NULL,0,NULL,NULL);WideCharToMultiByte(CP_ACP,0,wc_banner,wcslen(wc_banner),text,len,NULL,NULL);SetTextColor(hdc,cs->color_border);}}else {//单行模式int index_sel=0;if(-1!=(index_sel=ComboBox_GetCurSel(hwnd))) {ComboBox_GetLBText(hwnd,index_sel,text);SetTextColor(hdc,cs->color_text);}else if(hwnd!=GetFocus()){wchar_t wc_banner[256];ComboBox_GetCueBannerText(hwnd,wc_banner,sizeof(wc_banner));int len=WideCharToMultiByte(CP_ACP,0,wc_banner,wcslen(wc_banner),NULL,0,NULL,NULL);WideCharToMultiByte(CP_ACP,0,wc_banner,wcslen(wc_banner),text,len,NULL,NULL);SetTextColor(hdc,cs->color_border);}}SetBkMode(hdc,TRANSPARENT);cbi.rcItem.right=cbi.rcButton.left;DrawText(hdc,text,-1,&cbi.rcItem,DT_LEFT|DT_VCENTER);}else if((GetWindowLongPtr(hwnd,GWL_STYLE)&CBS_DROPDOWN)==CBS_DROPDOWN) {//带Edit的ComboBoxif(cs->is_multicheck) {//多选模式int length=ComboBox_GetMultiSelectText(hwnd,cs->plt);//SetTextColor(hdc,cs->color_text);if(length<=0&&hwnd!=GetFocus()) {wchar_t wc_banner[256];ComboBox_GetCueBannerText(hwnd,wc_banner,sizeof(wc_banner));int len=WideCharToMultiByte(CP_ACP,0,wc_banner,wcslen(wc_banner),NULL,0,NULL,NULL);WideCharToMultiByte(CP_ACP,0,wc_banner,wcslen(wc_banner),text,len,NULL,NULL);SetTextColor(hdc,cs->color_border);//SetBkMode(hdc,TRANSPARENT);cbi.rcItem.right=cbi.rcButton.left;DrawText(hdc,text,-1,&cbi.rcItem,DT_LEFT|DT_VCENTER);}}else {//单行模式int index_sel=0;if(-1!=(index_sel=ComboBox_GetCurSel(hwnd))) {//ComboBox_GetLBText(hwnd,index_sel,text);//SetWindowText(cbi.hwndItem,text);}else if(hwnd!=GetFocus()){//获取cuebanner.wchar_t wc_banner[256];ComboBox_GetCueBannerText(hwnd,wc_banner,sizeof(wc_banner));int len=WideCharToMultiByte(CP_ACP,0,wc_banner,wcslen(wc_banner),NULL,0,NULL,NULL);WideCharToMultiByte(CP_ACP,0,wc_banner,wcslen(wc_banner),text,len,NULL,NULL);SetTextColor(hdc,cs->color_border);SetBkMode(hdc,TRANSPARENT);cbi.rcItem.right=cbi.rcButton.left;DrawText(hdc,text,-1,&cbi.rcItem,DT_LEFT|DT_VCENTER);}}}EndPaint(hwnd,&ps);return 0;} break;case WM_NCPAINT:case WM_NCACTIVATE: {ComboBox_NCPaint(hwnd);return 0;} break;case WM_NCDESTROY: {ComboBox_ClearSettings(hwnd);}}return CallWindowProc(cs->pre_proc,hwnd,msg,wParam,lParam);
}int ComboBox_GetMultiSelectText(HWND hwnd,char* plt)
{    char* ptmp=plt;int count=ComboBox_GetCount(hwnd);for(int index=0;index<count;index++) {pComboBoxItem pdata=(pComboBoxItem)ComboBox_GetItemData(hwnd,index);if(pdata) {if(pdata->checked) {strcpy(ptmp,pdata->text);ptmp+=strlen(pdata->text);strcpy(ptmp,",");ptmp+=1;}   }}size_t length=ptmp-plt;if(length>0&&(*(ptmp-1)==',')) {length--;*(ptmp-1)=0;}else *plt=0;return length;
}int ComboBox_NCPaint(HWND hwnd)
{pComboBoxStyle cs=ComboBox_GetSettings(hwnd);if(!cs) return -1;//NC背景用client的DC进行填充HDC hdc=GetWindowDC(hwnd);RECT wnd_rc={0},title_rc={0},text_rc={0};//获取字体高度信息int cy=0;TEXTMETRIC tm={0};GetTextMetrics(hdc,&tm);cy=tm.tmHeight+tm.tmExternalLeading;GetWindowRect(hwnd,&wnd_rc);OffsetRect(&wnd_rc,-wnd_rc.left,-wnd_rc.top);FillRect(hdc,&wnd_rc,cs->brush);CopyRect(&title_rc,&wnd_rc);title_rc.right=title_rc.left+cs->margin_left;SetTextColor(hdc,cs->color_title);SetBkMode(hdc,TRANSPARENT);SelectObject(hdc,cs->font_title);//获取父窗口背景色HWND parent=GetParent(hwnd);if(parent) {//获取父窗口的背景色。通过WM_ERASEBKGND获取         //HDC parentdc=GetDC(parent);HBRUSH brush_title=CreateSolidBrush(RGB(0,0,0));//(HBRUSH)SendMessage(parent,WM_ERASEBKGND,(WPARAM)parentdc,0);FillRect(hdc,&title_rc,brush_title);DeleteObject(brush_title);}DrawText(hdc,cs->title,-1,&title_rc,DT_SINGLELINE|DT_VCENTER|DT_RIGHT);ReleaseDC(hwnd,hdc);InvalidateRect(hwnd,NULL,TRUE);return 0;
}int ComboBox_NCCalcSize(HWND hwnd,LPNCCALCSIZE_PARAMS calc)
{RECT rect_new;RECT rect_old;RECT client_rect_new;RECT client_rect_old;pComboBoxStyle cs=ComboBox_GetSettings(hwnd);   //调整非客户区的位置和大小//处理前//0:新 1:老 2:老客户区CopyRect(&rect_new,&(calc->rgrc[0]));CopyRect(&rect_old,&(calc->rgrc[1]));CopyRect(&client_rect_old,&(calc->rgrc[2]));HDC hdc=GetWindowDC(hwnd);RECT wnd_rc={0};//获取字体高度信息int cy=0,top_margin=0;;TEXTMETRIC tm={0};GetTextMetrics(hdc,&tm);cy=tm.tmHeight+tm.tmExternalLeading;top_margin=((rect_new.bottom-rect_new.top)-cs->font_cy)>>1;if(top_margin<=0) top_margin=0;//更新font_cy.cs->font_cy=cy;//处理后//0:新客户区 1:新 2:老client_rect_new = {rect_new.left+cs->margin_left,rect_new.top,rect_new.right,rect_new.bottom};CopyRect(&(calc->rgrc[0]),&client_rect_new);CopyRect(&(calc->rgrc[1]),&rect_new);CopyRect(&(calc->rgrc[2]),&rect_old);return WVR_VALIDRECTS;
}pComboBoxStyle ComboBox_GetSettings(HWND hwnd)
{pComboBoxStyle cs=(pComboBoxStyle)GetWindowLongPtr(hwnd,GWLP_USERDATA);return cs;
}int ComboBox_ClearSettings(HWND hwnd)
{pComboBoxStyle cs=ComboBox_GetSettings(hwnd);if(cs) {DeleteObject(cs->font);DeleteObject(cs->font_title);DeleteObject(cs->brush);DeleteObject(cs->brush_readonly);DeleteObject(cs->brush_border);DeleteObject(cs->pen);SetWindowLongPtr(hwnd,GWLP_WNDPROC,(LONG_PTR)cs->pre_proc);if(cs->plt) free(cs->plt);free(cs);}int count=ComboBox_GetCount(hwnd);while(count-->=0) {pComboBoxItem pdata=(pComboBoxItem)ComboBox_GetItemData(hwnd,count);free(pdata);}return 0;
}//已弃用 ComboBox_FindString(... 已有更加完善实现
int ComboBox_FindPrimString(HWND hwnd,char* prime_string)
{pComboBoxStyle cs=ComboBox_GetSettings(hwnd);if(!cs) return -1;int count=ComboBox_GetCount(hwnd);while(count-->=0) {pComboBoxItem pdata=(pComboBoxItem)ComboBox_GetItemData(hwnd,count);if(!pdata) continue;else if(strstr(pdata->text,prime_string)) {return count;}}return -1;
}int ComboBox_SetTitleOffset(HWND hwnd,int margin_left)
{pComboBoxStyle cs=ComboBox_GetSettings(hwnd);if(!cs) return -1;cs->margin_left=margin_left;RECT wnd_rc={0};GetWindowRect(hwnd,&wnd_rc);POINT pt={wnd_rc.left,wnd_rc.top};ScreenToClient(GetParent(hwnd),&pt);SetWindowPos(hwnd,HWND_BOTTOM,pt.x,pt.y,wnd_rc.right-wnd_rc.left,wnd_rc.bottom-wnd_rc.top,SWP_SHOWWINDOW|SWP_FRAMECHANGED);HWND ctrl=GetWindow(hwnd,GW_CHILD);char class_name[256]="";GetClassName(ctrl,class_name,sizeof(class_name));if(ctrl&&strstr(class_name,WC_EDIT)) {RECT rc={0};COMBOBOXINFO cbi={0};cbi.cbSize=sizeof(cbi);GetComboBoxInfo(hwnd,&cbi);GetClientRect(hwnd,&rc);MoveWindow(cbi.hwndItem,1,cbi.rcItem.top,rc.right-(rc.left+1)-(cbi.rcButton.right-cbi.rcButton.left),cbi.rcItem.bottom-cbi.rcItem.top,TRUE);       }else if(ctrl&&strstr(class_name,WC_STATIC)) {//MessageBox(hwnd,"Static","",MB_OK);}return 0;
}int ComboBox_InitialSettings(HWND hwnd,char* title)
{   pComboBoxStyle cs=(pComboBoxStyle)calloc(sizeof(RComboBoxStyle),1);if(!cs) return -1;cs->plt=(char*)calloc(sizeof(char),1024*10);cs->color_title=RGB(140,140,140);cs->color_text=RGB(0,120,250);cs->color_text_readonly=RGB(100,100,100);cs->color_border=RGB(70,70,70);//RGB(0,120,250);cs->color_readonly=RGB(35,35,35);cs->pen=CreatePen(PS_SOLID,1,cs->color_border);cs->font=CreateFont(17,0,0,0,FW_MEDIUM,//FW_SEMIBOLD,FALSE,FALSE,FALSE,DEFAULT_CHARSET,OUT_OUTLINE_PRECIS,CLIP_DEFAULT_PRECIS,CLEARTYPE_QUALITY, VARIABLE_PITCH,"Courier New");cs->font_title=CreateFont(17,0,0,0,FW_MEDIUM,//FW_SEMIBOLD,FALSE,FALSE,FALSE,DEFAULT_CHARSET,OUT_OUTLINE_PRECIS,CLIP_DEFAULT_PRECIS,CLEARTYPE_QUALITY, VARIABLE_PITCH,"微软雅黑");strcpy(cs->title,title);strcat(cs->title,":");SendMessage(hwnd,WM_SETFONT,(WPARAM)cs->font,0);cs->pre_proc=(WNDPROC)SetWindowLongPtr(hwnd,GWLP_WNDPROC,(LONG_PTR)ComboBoxOwnerProc);cs->color_bk=RGB(15,15,15);cs->brush=CreateSolidBrush(cs->color_bk);cs->brush_readonly=CreateSolidBrush(cs->color_readonly);cs->brush_border=CreateSolidBrush(cs->color_border);SetWindowLongPtr(hwnd,GWLP_USERDATA,(LONG_PTR)cs);ComboBox_SetItemHeight(hwnd,0,20);//设置所有项目高度ComboBox_SetItemHeight(hwnd,-1,18);//设置选中行高度//wchar_t string_buffer[256]={0};//swprintf(string_buffer,L"%s",L"请选择:");ComboBox_SetCueBannerText(hwnd,L"请选择:");return 0;
}int ComboBox_AddItem(HWND hwnd,char* text,BOOL checked)
{pComboBoxStyle cs=ComboBox_GetSettings(hwnd);if(!cs) return -1;pComboBoxItem pitem=(pComboBoxItem)calloc(sizeof(RComboBoxItem),1);if(!pitem) return -1;strcpy(pitem->text,text);pitem->checked=checked;int index=ComboBox_InsertItemData(hwnd,-1,pitem);ComboBox_SetItemData(hwnd,index,pitem);if(checked&&cs->is_multicheck) {char* plt=cs->plt;ComboBox_GetMultiSelectText(hwnd,plt);if((GetWindowLongPtr(hwnd,GWL_STYLE)&CBS_DROPDOWN)==CBS_DROPDOWN) {COMBOBOXINFO cbi={0};cbi.cbSize=sizeof(cbi);GetComboBoxInfo(hwnd,&cbi);Edit_SetText(cbi.hwndItem,plt);}InvalidateRect(hwnd,NULL,TRUE);}//只读,不支持非选项修改,但设置只读的Edit无法显示CueBanner.//弃用该方式,通过处理Edit的 EN_CHANGE 实现,以便保留CueBanner//Edit_SetReadOnly(cbi.hwndItem,TRUE);return 0;
}/*
改造WM_COMMAN/EN_CHANGE,实现自动补全
hwnd: combobox句柄
ctrl: combobox的edit句柄*/
int ComboBox_SingleLineComplete(HWND hwnd,HWND ctrl)
{char text[256]="";Edit_GetText(ctrl,text,sizeof(text));wchar_t string_buffer[256]={0};int wchar_len=MultiByteToWideChar(CP_ACP,0,text,strlen(text),NULL,0);MultiByteToWideChar(CP_ACP,0,text,strlen(text),string_buffer,wchar_len);string_buffer[wchar_len]='\0';int ib_sel=wcslen(string_buffer);//字符数量,使用wchar_t来处理。int index=ComboBox_FindString(hwnd,-1,text);if(index>=0) {pComboBoxItem pdata=(pComboBoxItem)ComboBox_GetItemData(hwnd,index);if(!pdata) return -1; ComboBox_SetCurSel(hwnd,index);int ie_sel=strlen(pdata->text);Edit_SetText(ctrl,pdata->text);SetFocus(ctrl);Edit_SetSel(ctrl,ib_sel,ie_sel);//有陷阱,字符index与字符index尽是坑。return 0;}return 1;
}LRESULT CALLBACK ComboTextOwnerProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{pLBParam pls=ComboLBox_GetSettings(hwnd);switch(msg) { case WM_SETFOCUS: {} break;case WM_CHAR: {//pComboBoxStyle cs=ComboBox_GetSettings(pls->combobox);//if(!cs) break;//if((!cs->alter_allowed)&&(!cs->is_multicheck)) return 0;} break;case WM_MOUSEWHEEL: {//pComboBoxStyle cs=ComboBox_GetSettings(pls->combobox);//if(!cs) break;//if((!cs->alter_allowed)&&(!cs->is_multicheck)) return 0;} break;case WM_RBUTTONDOWN: {return 0;//禁用右键菜单,暂时不支持      } break;case WM_NCDESTROY: {ComboLBox_ClearSettings(hwnd);} break;}return CallWindowProc(pls->pre_proc,hwnd,msg,wParam,lParam);
}int ComboBox_SetReadOnly(HWND hwnd,BOOL is_readonly)
{pComboBoxStyle cs=ComboBox_GetSettings(hwnd);if(cs) cs->alter_allowed=!is_readonly;if((!cs->is_multicheck)&&is_readonly) {cs->cur_index=ComboBox_GetCurSel(hwnd);}return 0;
}int ComboBoxCtrl_Test(HWND hwnd)
{HWND combobox1 = CreateWindowEx(NULL,WC_COMBOBOX,"",CBS_DROPDOWN|WS_CHILD|WS_TABSTOP|WS_OVERLAPPED|WS_VISIBLE|CBS_AUTOHSCROLL|CBS_OWNERDRAWFIXED,5, 5, 240, 25,hwnd,(HMENU)IDC_COMBOBOX_01,(HINSTANCE)GetWindowLongPtr(hwnd, GWLP_HINSTANCE), NULL);  ComboBox_InitialSettings(combobox1,"可选复选框");ComboBox_SetTitleOffset(combobox1,90);ComboBox_GetSettings(combobox1)->font_cy=17;ComboBox_GetSettings(combobox1)->is_multicheck=TRUE;{ComboLBox_InitialSettings(combobox1,"ComboLBox");ComboLBox_InitialSettings(combobox1,WC_EDIT);}ComboBox_AddData_Test(combobox1);ComboBox_SetReadOnly(combobox1,FALSE);HWND combobox2 = CreateWindowEx(NULL,WC_COMBOBOX,"",CBS_DROPDOWN/*|CBS_HASSTRINGS*/|WS_CHILD|WS_TABSTOP|WS_OVERLAPPED|WS_VISIBLE|CBS_AUTOHSCROLL|CBS_OWNERDRAWFIXED,5, 35, 240, 25,hwnd,(HMENU)IDC_COMBOBOX_02,(HINSTANCE)GetWindowLongPtr(hwnd, GWLP_HINSTANCE), NULL);  ComboBox_InitialSettings(combobox2,"只读复选框");ComboBox_SetTitleOffset(combobox2,90);ComboBox_GetSettings(combobox2)->font_cy=17;ComboBox_GetSettings(combobox2)->is_multicheck=TRUE;{ComboLBox_InitialSettings(combobox2,"ComboLBox");ComboLBox_InitialSettings(combobox2,WC_EDIT);}ComboBox_AddData_Test(combobox2);ComboBox_SetReadOnly(combobox2,TRUE);HWND combobox3 = CreateWindowEx(NULL,WC_COMBOBOX,"",CBS_DROPDOWN/*|CBS_HASSTRINGS*/|WS_CHILD|WS_TABSTOP|WS_OVERLAPPED|WS_VISIBLE,5, 65, 240, 25,hwnd,(HMENU)IDC_COMBOBOX_03,(HINSTANCE)GetWindowLongPtr(hwnd, GWLP_HINSTANCE), NULL);  ComboBox_InitialSettings(combobox3,"单选框");ComboBox_SetTitleOffset(combobox3,90);ComboBox_GetSettings(combobox3)->font_cy=17;ComboBox_GetSettings(combobox3)->alter_allowed=TRUE;{ComboLBox_InitialSettings(combobox3,WC_EDIT);}ComboBox_AddData_Test(combobox3);HWND combobox4 = CreateWindowEx(NULL,WC_COMBOBOX,"",CBS_DROPDOWN/*|CBS_HASSTRINGS*/|WS_CHILD|WS_TABSTOP|WS_OVERLAPPED|WS_VISIBLE,5, 95, 240, 25,hwnd,(HMENU)IDC_COMBOBOX_04,(HINSTANCE)GetWindowLongPtr(hwnd, GWLP_HINSTANCE), NULL);  ComboBox_InitialSettings(combobox4,"只读单选框");ComboBox_SetTitleOffset(combobox4,90);ComboBox_GetSettings(combobox4)->font_cy=17;ComboBox_GetSettings(combobox4)->is_multicheck=FALSE;{ComboLBox_InitialSettings(combobox4,WC_EDIT);}ComboBox_AddData_Test(combobox4);ComboBox_SetCurSel(combobox4,2);ComboBox_SetReadOnly(combobox4,TRUE);return 0;
}int ComboBox_AddData_Test(HWND hwnd)
{ComboBox_AddItem(hwnd,"0:第一个条目",FALSE);ComboBox_AddItem(hwnd,"1:第二个条目",TRUE);ComboBox_AddItem(hwnd,"2:第三个条目",FALSE);ComboBox_AddItem(hwnd,"3:第四个条目",TRUE);ComboBox_AddItem(hwnd,"4:第五个条目",FALSE);ComboBox_AddItem(hwnd,"5:第六个条目",TRUE);ComboBox_AddItem(hwnd,"6:第七个条目",FALSE);ComboBox_AddItem(hwnd,"7:第八个条目",TRUE);ComboBox_AddItem(hwnd,"SK0726:第八个条目",TRUE);ComboBox_AddItem(hwnd,"SMF001:第八个条目",TRUE);ComboBox_AddItem(hwnd,"SMX001:第八个条目",TRUE);ComboBox_AddItem(hwnd,"SMXA01:第八个条目",TRUE);return 0;
}

效果图

  1. 复选,可选模式
  2. 复选,只读模式:
  3. 单选,可编辑模式
  4. 单选,不可编辑模式

目录

Windows 标准控件 ComboBox 的改造

目的

代码

效果图


Windows 标准控件 ComboBox 的改造相关推荐

  1. 【免杀前置课——Windows编程】五、窗口控件——什么是控件、Windolws 窗口两大类、Windows标准控件/通用控件、控件响应的接收、创建窗口制作不同控件

    窗口控件 WINDOW控件 什么是控件? 控件是常见的窗口上的交互元素.例如:一个按钮,一个复选框,一个列表框等.当控件的特定功能被触发后,会主动发送消息通知父窗口,父窗口可以通过发送消息给控件控制控 ...

  2. windows标准控件的介绍与使用

    一. Windows标准控件概述 (一)Windows标准控件 Windows系统提供的标准控件主要包括静态控件.按钮控件.编辑框控件.列表框控件.组合框控件等,如表所示. Windows标准控件的类 ...

  3. MFC之使用 Windows 标准控件

    使用 Windows 标准控件 为了提高常用代码的复用性,VC 使用控件将常用的诸如用户输入.操作数据等功能封装起来.控件通常放在对话框或工具栏中,分为 3 种:Windows 标准控件.Active ...

  4. windows编程 标准控件的使用(按钮,文本框)

    文章目录 基本控件的使用 标准控件的创建 移动按钮 获取文本框的内容 自动设置文本框内容 设置父窗口 枚举出所有窗口 测试代码 基本控件的使用 标准控件的创建 在我们创建窗口并且在CREATE的时候创 ...

  5. win7 提示 由于无法验证发布者,windows阻止控件安装 解决办法

    本文转载自: https://www.cnblogs.com/philzhou/archive/2011/01/26/1945153.html 作者:philzhou 转载请注明该声明. 对于一些企业 ...

  6. KRBTabControl(中文)Windows选项卡控件

    本文阐述了如何在C#使自定义Windows选项卡控件. Download demo project - 82.4 KB Download source - 252 KB 介绍 本文讨论如何使用.NET ...

  7. Windows 公共控件库研究

    已知Windows公共控件库包含工具条控件,树视图控件,ListView控件:参见: https://blog.csdn.net/bcbobo21cn/article/details/10628767 ...

  8. Win32使用Windows公共控件库创建工具条

    Win32程序创建工具条,需要使用Windows公共控件库,是另一个DLL:本身Win32 API 是在系统自带的3个DLL: Win7,CFree 5.0:运行结果: 引入lib文件的设置如下: 如 ...

  9. 关于.Net 1.1 Windows Forms 控件的一个小问题

    不知道大家有没有注意到这样的一个问题,就是.Net 1.1的Windows Forms控件 的大小在不同环境下是不同的,比如,这个是在中文Windows2003 下用VS2003 英文版设计的界面: ...

最新文章

  1. FCGF-基于稀疏全卷积网络的点云特征描述子提取(ICCV2019)
  2. linux——(8)数据流重定向、管道命令
  3. ButterKnife不同版本配置
  4. CSS的outline轮廓属性:轮廓属性
  5. docker和虚拟机的区别_详解win7操作系统下安装部署Docker环境
  6. linux是只读添加 来覆盖,Linux之指令 重定向 文件覆盖和文件追加
  7. 姑娘,你为什么要编程?
  8. ajax php 观察者模式,JavaScript观察者模式定义和dom事件实例详解
  9. 手机按键中控运行思路的个人理解
  10. 不可重入锁和可重入锁
  11. 面试进阶题集锦-持续更新
  12. 智能家居简单实现---使用ESP8266简单实现和APP通讯
  13. 设计模式(七): 通过转接头来观察适配器模式(Adapter Pattern)
  14. zabbix监控nginx的状态
  15. smartadmin mysql_Smart Admin
  16. 免费的网络验证系统插件
  17. VBA操作WORD(五)批量调整图片大小、居中设置
  18. 适合客厅的挂画 山水画让家活色生香
  19. python扫描局域网ip_Python实现扫描局域网活动ip
  20. 【Typora启动报错】This beta version of Typora is expired, please download and install a newer version.

热门文章

  1. 判断并求出两个圆的交点(平面几何)
  2. ArcGIS中统计渔网中栅格人口密度
  3. 分支预测优化之__builtin_expect
  4. Ray-分布式的SGD
  5. 微软急疯了?部分用户称PC自动升级到Win10
  6. MuleSoft知识总结-4.使用RAML设计接口
  7. 我跑了香港六家银行,把境外开户的事整明白了
  8. Qt 添加界面背景图片
  9. 010Editor十六进制转图片(攻防演练-决定用和决一死战)
  10. 医学图像分割知识点总结