【Windows编程】创建基本控件

前一篇文章我们一起学习了Windows编程基本框架,几乎所有的Windows编程都是以这个模式开始,剩下的就是如何怎么框架的基础上如何添加枝叶实现不同的功能了。在Windows平台下,肯定少不了最基本的界面控件,这些控件方便了应用程序与用户之间的交互,也成了Windows独霸个人PC市场近20年的关键因素。

在Win32 SDK环境下,怎么来创建常用的那些基本控件呢?我们知道如果用MFC,简单的拖放即可完成大多数控件的创建,但是我们既然是用Windows SDK API编程,当然是从根上解决这个问题,实际上MFC的下层也是通过这些API完成的。

实际上控件也是窗口,只不过是被微软预先创建好的一种窗口类,比如button就是一个类名为“BUTTON”的类。既然控件实际上是窗口,当然也是用CreateWindow或者CreateWindowEx这个函数来完成的,第二个函数除了多一个扩展风格之外,其他完全一样,下面以主要以CreateWindow为例说明,该函数原型如下(实际上在第一篇已经讲过了):

HWND CreateWindow(LPCTSTR lpClassName,

LPCTSTR lpWindowName,

DWORD dwStyle,

int x, int y, int nWidth, int nHeight,

HWND hWndParent,

HMENU hMenu,

HANDLE hInstance,

PVOID lpParam );

lpClassName:控件的类名

lpWindowName:控件的名字或者文本

dwStyle:控件风格

x,y,nWidth,nHeight:控件的坐标和宽高

hWndParent:控件的父窗口

hMenu:控件的ID

hInstance:控件设置为空

lpParam:控件设为空

那么具体怎么实现各个基本的控件呢?下面我们来先依次举例。

  • 创建标签/静态文本

CreateWindow(TEXT("STATIC"), TEXT("Test String"), WS_CHILD|WS_VISIBLE, 10, 10, 80, 20, hWnd, (HMENU)IDC_STATIC1, NULL, NULL);

  • 创建button

CreateWindow(TEXT("BUTTON"), TEXT("Click Me"), WS_CHILD|WS_VISIBLE, 10, 40, 80, 20, hWnd, (HMENU)IDC_BUTTON1, NULL, NULL);

  • 创建编辑框

CreateWindowEx(WS_EX_CLIENTEDGE, TEXT("EDIT"), NULL, WS_CHILD|WS_VISIBLE|WS_BORDER, 10, 70, 80, 20, hWnd, (HMENU)IDC_EDIT1, NULL, NULL);

  • 创建Groupbox

CreateWindow(TEXT("BUTTON"), TEXT("frame"), WS_CHILD|WS_VISIBLE|BS_GROUPBOX, 120, 10, 130, 50, hWnd, (HMENU)IDC_FRAME1, NULL, NULL);

  • 创建单选框

CreateWindow(TEXT("BUTTON"), TEXT("radio button"), WS_CHILD|WS_VISIBLE|BS_AUTORADIOBUTTON, 134, 30, 40, 20, hWnd, (HMENU)IDC_MALE, NULL, NULL);

  • 创建复选框

CreateWindow(TEXT("BUTTON"), TEXT("红色"), WS_CHILD|WS_VISIBLE|BS_CHECKBOX|BS_AUTOCHECKBOX, 120, 70, 54, 20, hWnd, (HMENU)IDC_CKBRED, NULL, NULL);

  • 创建列表框

CreateWindowEx(WS_EX_CLIENTEDGE, TEXT("LISTBOX"), NULL, WS_CHILD|WS_VISIBLE|LBS_STANDARD, 10, 100, 80, 90, hWnd, (HMENU)IDC_LISTBOX1, NULL, NULL);

  • 创建组合框

CreateWindow(TEXT("COMBOBOX"), NULL, WS_CHILD|WS_VISIBLE|WS_VSCROLL|CBS_AUTOHSCROLL|CBS_DROPDOWNLIST, 120, 100, 100, 90, hWnd, (HMENU)IDC_CB1, NULL, NULL);

  • 创建滚动条

CreateWindow(TEXT("SCROLLBAR"), NULL, WS_CHILD|WS_VISIBLE|SBS_HORZ, 10, 200, 200, 20, hWnd, (HMENU)IDC_SCB1, NULL, NULL);

以上创建各个控件都很差傻瓜化,你可以复制下来改改坐标、文本、风格、ID等就可以按照你的需要创建了。

下面我们就来实战一下,把这些常用的控件在程序中实现,下面我们来实现一个完整的windows程序,程序中包含了这些常用的控件和更多的用法。

/* 常用控件实例 */

#include <windows.h>

#define IDC_STATIC1  1001
#define IDC_BUTTON1  1002
#define IDC_EDIT1    1003
#define IDC_FRAME1   1004
#define IDC_MALE     2001
#define IDC_FEMALE   2002
#define IDC_LISTBOX1 1005
#define IDC_CB1      1006
#define IDC_CKBRED   1007
#define IDC_CKBGRN   1008
#define IDC_CKBBLU   1009
#define IDC_SCB1     1010
#define IDC_SCB2     1011

static TCHAR szAppName[] = TEXT("HelloWin32");
static LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
   HWND     hWnd;
   MSG      msg;
   WNDCLASS wndclass;

   wndclass.style         = CS_HREDRAW | CS_VREDRAW;
   wndclass.lpfnWndProc   = WndProc;
   wndclass.cbClsExtra    = 0;
   wndclass.cbWndExtra    = 0;
   wndclass.hInstance     = hInstance;
   wndclass.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
   wndclass.hCursor       = LoadCursor(NULL, IDC_ARROW);
   wndclass.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
   wndclass.lpszMenuName  = NULL;
   wndclass.lpszClassName = szAppName;

   if (!RegisterClass(&wndclass))
   {
      MessageBox (NULL, TEXT("This program requires Windows NT!"), szAppName, MB_ICONERROR);
      return 0;
   }

   hWnd = CreateWindow(szAppName,// class name
             szAppName,  // window caption
             WS_OVERLAPPEDWINDOW,// window style
             CW_USEDEFAULT, // initial x position
             CW_USEDEFAULT, // initial y position
             400, // initial x size
             300, // initial y size
             NULL,// parent window handle
             NULL,// window menu handle
             hInstance,// program instance handle
             NULL);// creation parameters

   ShowWindow(hWnd, iCmdShow);
   UpdateWindow(hWnd);

   while (GetMessage(&msg, NULL, 0, 0))
   {
      TranslateMessage(&msg);
      DispatchMessage(&msg);
   }

   return msg.wParam;
}

static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
   HWND hCtrl;

   switch (message)
   {
   case WM_CREATE:
      CreateWindow(TEXT("STATIC"), TEXT("Test String"), WS_CHILD|WS_VISIBLE, 10, 10, 80, 20, hWnd, (HMENU)IDC_STATIC1, NULL, NULL);

      CreateWindow(TEXT("BUTTON"), TEXT("Click Me"), WS_CHILD|WS_VISIBLE, 10, 40, 80, 20, hWnd, (HMENU)IDC_BUTTON1, NULL, NULL);
      CreateWindowEx(WS_EX_CLIENTEDGE, TEXT("EDIT"), NULL, WS_CHILD|WS_VISIBLE|WS_BORDER,  10, 70, 80, 20, hWnd, (HMENU)IDC_EDIT1, NULL, NULL);

      CreateWindow(TEXT("BUTTON"), TEXT("性别"), WS_CHILD|WS_VISIBLE|BS_GROUPBOX, 120, 10, 130, 50, hWnd, (HMENU)IDC_FRAME1, NULL, NULL);
      CreateWindow(TEXT("BUTTON"), TEXT("男"), WS_CHILD|WS_VISIBLE|BS_AUTORADIOBUTTON, 134, 30, 40, 20, hWnd, (HMENU)IDC_MALE, NULL, NULL);
      CreateWindow(TEXT("BUTTON"), TEXT("女"), WS_CHILD|WS_VISIBLE|BS_AUTORADIOBUTTON, 194, 30, 40, 20, hWnd, (HMENU)IDC_FEMALE, NULL, NULL);

      CreateWindow(TEXT("BUTTON"), TEXT("红色"), WS_CHILD|WS_VISIBLE|BS_CHECKBOX|BS_AUTOCHECKBOX, 120, 70, 54, 20, hWnd, (HMENU)IDC_CKBRED, NULL, NULL);
      CreateWindow(TEXT("BUTTON"), TEXT("绿色"), WS_CHILD|WS_VISIBLE|BS_CHECKBOX|BS_AUTOCHECKBOX, 180, 70, 54, 20, hWnd, (HMENU)IDC_CKBGRN, NULL, NULL);
      CreateWindow(TEXT("BUTTON"), TEXT("蓝色"), WS_CHILD|WS_VISIBLE|BS_CHECKBOX|BS_AUTOCHECKBOX, 240, 70, 54, 20, hWnd, (HMENU)IDC_CKBBLU, NULL, NULL);

      hCtrl = CreateWindowEx(WS_EX_CLIENTEDGE, TEXT("LISTBOX"), NULL, WS_CHILD|WS_VISIBLE|LBS_STANDARD, 10, 100, 80, 90, hWnd, (HMENU)IDC_LISTBOX1, NULL, NULL);
      SendMessage(hCtrl, LB_ADDSTRING, 0, (LPARAM)TEXT("list str1"));
      SendMessage(hCtrl, LB_ADDSTRING, 0, (LPARAM)TEXT("list str2"));
      SendMessage(hCtrl, LB_ADDSTRING, 0, (LPARAM)TEXT("list str3"));
      SendMessage(hCtrl, LB_ADDSTRING, 0, (LPARAM)TEXT("list str4"));

      hCtrl = CreateWindow(TEXT("COMBOBOX"), NULL, WS_CHILD|WS_VISIBLE|WS_VSCROLL|CBS_AUTOHSCROLL|CBS_DROPDOWNLIST, 120, 100, 100, 90, hWnd, (HMENU)IDC_CB1, NULL, NULL);
      SendMessage(hCtrl, CB_ADDSTRING, 0, (LPARAM)TEXT("comb str1"));
      SendMessage(hCtrl, CB_ADDSTRING, 0, (LPARAM)TEXT("comb str2"));
      SendMessage(hCtrl, CB_ADDSTRING, 0, (LPARAM)TEXT("comb str3"));
      SendMessage(hCtrl, CB_ADDSTRING, 0, (LPARAM)TEXT("comb str4"));
      SendMessage(hCtrl, CB_SETCURSEL, 1, 0);

      CreateWindow(TEXT("SCROLLBAR"), NULL, WS_CHILD|WS_VISIBLE|SBS_HORZ, 10, 200, 200, 20, hWnd, (HMENU)IDC_SCB1, NULL, NULL);
      CreateWindow(TEXT("SCROLLBAR"), NULL, WS_CHILD|WS_VISIBLE|SBS_VERT, 240, 100, 20, 120, hWnd, (HMENU)IDC_SCB2, NULL, NULL);
      return 0;

   case WM_COMMAND:
   {
      HWND hwndTmp;
      int wmId = LOWORD(wParam);
      switch (wmId)
      {
         case IDC_BUTTON1:
         hwndTmp = GetDlgItem(hWnd, IDC_STATIC1);
         SetWindowText(hwndTmp, TEXT("New Text"));
         MessageBox(hWnd, TEXT("Text Changed!"), TEXT("info"), MB_OK);
         break;
      }
   }
   return 0;

   case WM_CTLCOLORSTATIC:
   {
      HDC hdc = (HDC)wParam;
      SetTextColor(hdc, RGB(0xFF, 0x00, 0x00));
   }
   return (BOOL)((HBRUSH)GetStockObject(NULL_BRUSH));

   case WM_DESTROY:
      PostQuitMessage(0);
      return 0;
   }

   return DefWindowProc (hWnd, message, wParam, lParam);
}

编译一下运行,结果如下:

怎么样,是不是很简单,虽然我们的控件创建比MFC麻烦一下,但是我们是从“根”上来解决问题的,感觉是不一样的!有了这些基本的用法,你就可以构建更加复制的Windows界面了。赶快去试试吧。

原文发布于微信公众号 - 程序员互动联盟(coder_online)

原文发表时间:2015-06-28

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Netkiller

数据库记录安全解决方案

目录 1. 什么是防删除,防撰改 2. 为什么要做防删除,防撰改限制 3. 何时做防删除,防撰改限制 4. 在哪里做防删除,防撰改限制 5. 谁去做防删除,防撰...

2725
来自专栏杨建荣的学习笔记

关于操作失误的数据修复(r2笔记48天)

在系统升级的过程中,准备了大量的脚本,分成几个窗口来分别执行。 在碰到问题的时候,一定要很细心和冷静,不经意的错误可以需要几倍,几十倍的努力来挽回。 准生产环境...

3305
来自专栏杨建荣的学习笔记

通过shell脚本模拟MySQL自增列的不一致问题

MySQL的自增列问题其实很有意思,在重启数据库之后,会按照max(id)+1的方式来计算,这样一个看起来有些别扭的实现方式在早期版本就饱受诟病,在MyS...

2844
来自专栏Netkiller

MySQL·身份证校验

本文节选自《Netkiller Architect 手札》 4.17. 数据检查 4.17.1. 身份证校验 该函数能够检查身份证号码是否正确 CREATE D...

3515
来自专栏编程之路

教师听课评课系统设计

需求分析:管理教师安排任课教师课程,安排听课教师,听课教师到教室听课,使用手机记录学生表现、教师表现、综合表现、以及建议,告别纸质化。课后授课教师查看所有听课人...

783
来自专栏编程之路

教师听课评课系统设计

需求分析:管理教师安排任课教师课程,安排听课教师,听课教师到教室听课,使用手机记录学生表现、教师表现、综合表现、以及建议,告别纸质化。课后授课教师查看所有听课人...

1944
来自专栏Netkiller

PostgreSQL·账户表/余额表/消费储蓄表

本文节选自《Netkiller Architect 手札》 4.22.3. 账户表/余额表/消费储蓄表 此表适用于购物车等金钱来往账面等等。 -- Tabl...

2555
来自专栏菩提树下的杨过

无限级分类(非递归算法/存储过程版/GUID主键)完整数据库示例_(1)表结构

无限分类是一个老生常谈的话题了,网上有很多解决方案,可以分成二个流派,一种利用递归,一种利用非递归(当然需要其它一些辅助手段判断节点层次),但核心表结构都差不多...

1736
来自专栏杨建荣的学习笔记

使用SQL来分析数据库参数(二)(r10笔记第82天)

拖延症的我终于接下来第二篇数据库参数的分析。 数据库的参数分析一直以来是调优中的重要一环,而感觉有时候却感觉找不到一些方法,我分析了一下,还是蛮有意思。数据库的...

2618
来自专栏杨建荣的学习笔记

关于desc的一个奇怪问题及分析(r2第18天)

在平时的工作中,desc这个命令可谓短小精悍,可以很方便的查看表结构和not null的情况。 今天在生产环境中碰到一个有些奇怪的desc问题。 首先是数据迁移...

2605

扫描关注云+社区