Timers and Animation

theForger's Win32 API教程第二版(简体中文)

主页

基础
  1. 开始学习
  2. 一个简单的窗口
  3. 处理消息
  4. 理解消息循环
  5. 使用资源
  6. 菜单和图标
  7. 对话框
  8. 非模态对话框
  9. 标准控件
  10. 对话框常见问题
创建一个简单应用
  1. 在运行时创建控件
  2. 文件与常用对话框
  3. 工具栏与状态栏
  4. 多文档界面
图形设备接口
  1. 位图,设备上下文
  2. 透明位图
  3. 定时器与动画
  4. 文本,字体与顏色
工具与文档
  1. 参考
  2. 免费的Visual C++(最新更新)
附表
  1. 常见错误的解決方法
  2. API vs. MFC
  3. 关于资源文件的说明

标准控件:按钮,编辑框,列表框

[images/ctl_one.gif]

范例: ctl_one

  我注意到我已经在前面的例子中用过按钮了,所以你们对其或多或少地应该有些了解了,但是我想到我要在此例子中使用它们,我还是在标题中写了它,以求完整.

控件

对于控件要记住的一件事就是它们就是窗口.如同任何其它的窗口,他们有窗口过程,窗口类,等等...由系统注冊.任何你可以对一个窗口做的事情你都可以对一个控件做.

消息

  回忆一下我们先前讲的消息循环,窗口用消息通信,你发送它们来让一个控件做点什么,当控件发生一个事件的时候它也向你发送一个通知消息.对于 标准控件这个通知是一个WM_COMMAND消息,就如我们在按钮和菜单中已经见到过的一样..对于后面要讲的常见控件,它将会是WM_NOTIFY.

  对于不同的控件你发送相当不同的消息,每个控件都有自己的消息集合.有时一个消息可用于多个控件,但一般它们只会在一个它们专门面向的控件上工作.对于列表框(listbox) 和组合框(combobox)的消息(LB_*和CB_*)这一点尤其令人伤脑筋,它们几乎完成相同的功能,但是卻不能互換,我自己也经常被搞混,次数多得我不好意思承认了:)

  另外一方面,像WM_SETTEXT的通用消息则为大多数控件所支持.毕竟一个控件就是一个窗口而已.

  你可以用SendMessage()这个API来发送消息,用getDlgItem()来获取控件的句柄,或者你可以用SendDlgItemMessage()来一步完成上面的两步操作,两种方式的结果是等效的.

编辑框

  编辑框是Windows系统中最常用的一个控件,用来让用戶来对文本进行输入,修改,复制,等等操作. Windows中的记事本就差不多是一个平凡的窗口再在里面嵌上一个大大的编辑框.

  这就是此例中用来和编辑框接口的代码:

    SetDlgItemText(hwnd, IDC_TEXT, "This is a string");

  这就是用来攺变控件中的文字所需的全部代码(可用于几乎所有有相关的文本值的控件,靜态控件,按钮等等).

  从控件获取文本也是一样的简单,虽然多了一点点的设置要做...

    int len = GetWindowTextLength(GetDlgItem(hwnd, IDC_TEXT));
    if(len > 0)
    {
        int i;
        char* buf;

        buf = (char*)GlobalAlloc(GPTR, len + 1);
        GetDlgItemText(hwnd, IDC_TEXT, buf, len + 1);

        //... do stuff with text ...

        GlobalFree((HANDLE)buf);
    }

  首先我们要为要存进来的字符串配置一点內存,它不会就返回一个指向內存中已有的字符串的指针.为了做这一点,首先我们要弄清楚为要配置多少內存.沒有GetDlgItemTextLength()这个API,但是有一个GetWindowTextLength(),所以我们要自己使用GetDlgItem()获取控件的句柄.

  现在我们获取了长度,就可以配置內存了. 这里我添加了一个小小的检查来看是否有文本要处理,因为你很可能并不想处理一个空的字符串...有时候你要这样做,那也是你自己的事.假设你有些东西要处理,我们调用GlobalAlloc()来配置一些內存. 我在这里用的GlobalAlloc()等价于calloc(),如果你对DOS/UNIX下的编程的话很熟悉的话.它分配一点內存,将其初始化为0并返回这段內存的指针.对于第一个参数,你可以传不同的值让它为不同的目的来做不同的操作,但在此教程中我只会用这一种方式.

  请注意我在两个位置对长度加了1,这是干什么?因为GetWindowTextLength()返回控件的字符个数中不包括末尾那个终止符.这就意味著如果我们不加1就配置內存来存储这个字符串的话,字符串可以放下,但是终止符就会溢出內存块,可能会破坏別的数据,导致访问冲突,或是別的什么坏的结果.在处理windows系统中的字符串长度时候你要格外小心,一些API和函数要求字符串长度包括终止符但另外一些要求不要,随时準备查文档.

  如果我在这里沒有讲到终止符的话,请参考一本C语言的课本或是基础教材,它们会讲这个.

  最后我们用GetDlgItemText()来将控件的內容获取至我们刚刚配置好的缓冲区.这个调用则要求缓冲区的长度包括终止符.它的返回值是复制的字符个数,我们这里将其忽略了,但这个数字又不包括终止符...搞笑吧?:)

  我们全部处理完了文本后(马上就要到了),我们要我们刚刚配置的內存释放掉以免它们泄漏了,又落到CPU上就会把计算机搞短路.我们调用GlobalFree(),传进我们的指针就可以完成这个任务了.

  你可能会见到过或想像有另外一套API,叫作LocalAlloc(),LocalFree(),等等...它们是16位windows系统的历史API.在Win32平台中,Local*和Global*两种內存相关函数完全是一样的.

编辑框处理数字

  可以输入文本了,但是如何让用戶来输入数字?这是一个很常见的任务,幸好有一个API来简化这项工作,它为你做所有的內存配置,并把字符串转为整型数.

    BOOL bSuccess;
    int nTimes = GetDlgItemInt(hwnd, IDC_NUMBER, &bSuccess, FALSE);

  GetDlgItemInt()和GetDlgItemText()很相似,除了后者是把一个字符串拷贝到一个缓冲区中去,而前者是把一个字符串在內部转为整型数并把值返回给你.第三个参数是可选的,是一个指向BOOL型的指针.因为这个函数在失败的时候返回0,所以沒有办法来区分究竟是调用失败了还是用戶输入了一个0.如果你不介意用0表示一个调用失败,那尽管忽略这个参数吧.

  另外一个有用的参数是编辑框控件的ES_NUMBER风格,这个风格可以限制只允许用戶输入0到9的数字.当你只需要正数输入的时候这个功能尤其方便,其它的情況下不怎么样,因为它不让你输入任何其它的字符,包括-(负号).(小数点),(分隔符).

列表框

  另外一个经常用的控件是列表框.这是我在这里準备讲的最后一个标准控件,因为实话说,它们不是是很有趣,如果你还沒有感到枯燥的话,我已经有点了:)

加一个列表项

  你想对一个列表框做的第一件事就是给它加一个项.

    int index = SendDlgItemMessage(hwnd, IDC_LIST, LB_ADDSTRING, 0, (LPARAM)"Hi there!");

  你可以看到,这是一件很简单的任务.如果列表框指定了LBS_SORT风格,新项将以字母顺序加进去,否则它将被加到列表的尾部.

  这条消息总是返回新添加的列表项的序号,我们可以用这个序号来对列表项进行別的一些操作,比如向其绑定一些数据.一般会是一个指向一个句含更多消息的结构体的指针,或一个你用来标识这个列表项的标识,由你定了.

    SendDlgItemMessage(hwnd, IDC_LIST, LB_SETITEMDATA, (WPARAM)index, (LPARAM)nTimes);

通知

  列表框的整个意义在于让用戶从一个列表选择东西.现在我们不是很关心他们具体什么时候来做选择,比如对于我们的删除按钮,我们不需要知道什么时候选择攺变了,我们只需要在用戶激活这个按钮的时候检查就是了.

  然而,有些时候你想马上响应,比如根据所选不同的列表项来作不同的显示或更新某些消息. 为此我们要响应列表框给我们发的通知消息. 本例中我们要注意LBN_SELCHANGE,这个通知消息告诉我们用戶更改了所选的列表项目.LBN_SELCHANGEWM_COMMAND发出,不过按钮或菜单的WM_COMMAND消息处理(一般就是响应点击)所不同的是,列表框发出WM_COMMAND消息的原因很多,所以我们要做进一步的检查找出更多的消息. 通知代码在wParamHIWORD部分传递,wParam的另外一半则告诉我们控件的ID

    case WM_COMMAND:
        switch(LOWORD(wParam))
        {
            case IDC_LIST:
                // It's our listbox, check the notification code
                switch(HIWORD(wParam))
                {
                    case LBN_SELCHANGE:
                        // Selection changed, do stuff here.
                    break;
                }
            break;
            // ... other controls
        }
    break;

从列表框中获取数据

  一旦我们知道选择己被攺变了,或者应用戶要求,我们就需要从列表框中获取选择的结果并做点有用的处理.

  本例中我们使用了一个多选的列表框,所以获取所选的项的清单就要点小技巧.如果用一个单选框,那你就简单地发送一个LB_GETCURSE消息就可以获得所选项的序号.

  首先我们要获得所选项的数目,据此我们才能为我们要保存的序号来配置內存.

    HWND hList = GetDlgItem(hwnd, IDC_LIST);
    int count = SendMessage(hList, LB_GETSELCOUNT, 0, 0);

然后根据所选的列表项数目来配置內存,并发送LB_GETSELITEMS去填充数组.

    int *buf = GlobalAlloc(GPTR, sizeof(int) * count);
    SendMessage(hList, LB_GETSELITEMS, (WPARAM)count, (LPARAM)buf);

    // ... Do stuff with indexes

    GlobalFree(buf);

  本例中,buf[0]是第一个序号,一直到buf[count-1]存放后面的.

  你可能还想从这些所选中的列表项中,获取跟它们相绑定的数据,并作一些处理.这跟当初设置它们一样简单,发送另外一个消息就是了.

    int data = SendMessage(hList, LB_GETITEMDATA, (WPARAM)index, 0);

  如果数据为某种类型的数值(任何32位的)你就可以简单地強制将其转为相应的类型.比如你可以用HBITMAP来代替int存放返回值...

    HBITMAP hData = (HBITMAP)SendMessage(hList, LB_GETITEMDATA, (WPARAM)index, 0);

靜态控件

  跟按钮一样,靜态控件也很简单,但为了完整起见,我还是把它们写在了这里.靜态控件一般就是很安靜,意思是它们一般不攺变或做什么特別的事,主要就是用来向用戶显示文本.但是你可以向其指定一个独特的标识使它稍微有用些(VC++给它们指定默认的标识IDC_STATIC,其实就是-1,表示”沒有标识”),这样就可以在运行的时候设置文字来向用戶动态地显示数据.

  在范例代码中,我用一个靜态控件来显示选择的列表项的数据,假定你一次只选一个列表项:

    SetDlgItemInt(hwnd, IDC_SHOWCOUNT, data, FALSE);

Copyright © 1998-2008, Brook Miles (forgey). All rights reserved.