【Windows编程】系列第六篇:创建Toolbar与Statusbar

上一篇我们学习了解了如何使用Windows GDI画图,该应用程序都是光光的静态窗口,我们使用Windows应用程序,但凡稍微复杂一点的程序都会有工具栏和状态栏,工具栏主要用于一些快捷功能按钮。比如典型的windows应用程序的上面是菜单栏,从菜单栏我们可以选择应用程序提供的各种功能,但是有的功能比较常用,且不能放在第一级菜单,需要进入二级、三级甚至更多的菜单才能选择。显然这样使用起来比较麻烦,于是这时候工具栏的作用就体现出来了,一般工具栏位于菜单栏的下面,但是位于客户窗口的上面。下面就是windows的文本编辑器的工具栏:

Statusbar主要用于显示应用程序的运行状态,统计信息,操作信息等提示作用,一般是只读状态。典型的状态栏放在窗口的最下面,比如下面就是我正在使用的Word应用程序的状态栏:

我们这次要一起学习的就是使用纯Windows API函数创建基本的状态栏和菜单栏。如果属性MFC的朋友知道,如果用MFC来做工具栏和菜单栏很简单。但是使用纯API就麻烦一些了,当然带来的感觉是不一样的,比如要动态创建,使用MFC的资源编辑器就无能为力,但是对于我们今天要使用的API创建方式来说,就显示出他的强大了。

首先,前面我们在讲解Windows常用控件的创建时就知道,所有带窗口的控件创建实际上都是调用Windows提供的CreateWindow或者CreateWindowEx(以下以CreateWindowEx为例)这两个函数,当然今天的菜单栏和状态栏一样需要这两个函数来创建。由于工具栏和状态栏都是Windows的通用控件组中的控件,有默认的类名。创建工具栏时,类名为TOOLBARCLASSNAME,创建状态栏时,类名为STATUSCLASSNAME,这两个宏定义在commctrl.h文件中根据是否使用UNICODE编码分别是“ToolbarWindow32”和“msctls_statusbar32”的宽字符版本和ANSI版本。

  • 创建工具栏

除了使用CreateWindowEx创建好工具栏后,作为一个更好看一点,我们还可以给工具栏加上图标以及功能提示。为了加图标,最方便的方法是使用一些列图像列表API来加载和管理工具栏的图片。ImageList_Create可以创建一个图片列表,它的原型为:

HIMAGELIST ImageList_Create(int cx, int cy, UINT flags,  int cInitial, int cGrow);

该函数用法在MSDN上说的比较清楚。这个列表创建后并没有图片,只是一个列表管理的容器,还需要加载图片集。加入图片所需API如下:

int ImageList_AddMasked(HIMAGELIST himl, HBITMAP hbmImage, COLORREF crMask);

图片加载后,还可以设置图片的显示属性,包括显示图片、文本以及信息提示功能。工具按钮的响应是通过向窗口处理程序发送WM_COMMAND消息实现的,有用户处理按钮事件。工具栏的提示信息是通过WM_NOTIFY消息,由用户设置,工具栏的创建实例请参看后面的应用实例。

  • 创建状态栏

装具状态栏相比工具栏要简单很多,用CreateWindowEx建立状态栏后,默认情况下,状态栏只有一个显示面板(panel),要创建多个面板,只要向状态栏把配置好个面板的长度发送SB_SETPARTS消息即可,消息参数分别是面板个数和个面板的终点数组。比如:

int array[3]={120,120*2,-1};
SendMessage(hWndStatus,SB_SETPARTS,(WPARAM)3,(LPARAM)array);

表示将状态栏面板分为3各部分,第一部分到120像素为止,第二部分到240像素为止,剩下的(-1)全部分到第三部分中。

设置面板文本内容,可以给状态栏发送SB_SETTEXT消息;要在状态栏面板中增加小图标,可以给状态发送SB_SETICON消息。

上面的工具栏和状态栏只是介绍了最基本的情况,更多的设置和消息处理都可以参考MSDN。本系列专注在基本编程和使用上,只是介绍常见的用法,不过这些用法也够初学者编程使用。

  • 使用实例

下面我们通过一个基本的实例程序说明如何采用纯Windows API创建、使用工具栏和状态栏。程序中创建的Toolbar有三个图标,当程序正常显示,鼠标在某个工具栏按钮上移动,或者处于Disable状态是,会有不同的图片显示,读者可以自己更换其他自己喜欢的图标。

#include <windows.h>
#include <commctrl.h>
#include <tchar.h>

#pragma comment(lib, "comctl32.lib")

// Windows XP sytle button
#pragma comment(linker,"\"/manifestdependency:type='win32' "\
						"name='Microsoft.Windows.Common-Controls' "\
						"version='6.0.0.0' processorArchitecture='*' "\
						"publicKeyToken='6595b64144ccf1df' language='*'\"")

#define IDC_TOOLBAR   1001
#define IDC_STATUSBAR 1002
//#define PIC_RESOURCE_USED

#ifdef PIC_RESOURCE_USED
#define IDB_NEW   110
#define IDB_OPEN  111
#define IDB_SAVE  112
#endif
#define ID_FOPEN  1111
#define ID_FCLOSE 1112
#define ID_FSAVE  1113

static TCHAR szAppName[] = TEXT("toolbar");
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;
     }
     //初始化公共空间
     INITCOMMONCONTROLSEX icc;
     icc.dwSize = sizeof(INITCOMMONCONTROLSEX);
     icc.dwICC = ICC_BAR_CLASSES;
     InitCommonControlsEx(&icc);
     hWnd = CreateWindow(szAppName,szAppName,WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,400,300,NULL,NULL,hInstance,NULL);
     ShowWindow(hWnd, iCmdShow);
     UpdateWindow(hWnd);
     
     while (GetMessage(&msg, NULL, 0, 0))
     {
          TranslateMessage(&msg);
          DispatchMessage(&msg);
     }

     return msg.wParam;
}

HWND CreateToolbar(HWND hParentWnd)
{
    HWND hWndTB;
    TBBUTTON tbb[3];
    HIMAGELIST hImageList,hHotImageList,hDisableImageList;
    HBITMAP hBitmap;

    HINSTANCE hInst = GetModuleHandle(NULL);
    //创建Toolbar控件
    hWndTB = CreateWindowEx(0, TOOLBARCLASSNAME,TEXT(""),WS_CHILD|WS_VISIBLE|WS_BORDER|TBSTYLE_LIST|TBSTYLE_AUTOSIZE|TBSTYLE_TOOLTIPS,0,0,0,0,hParentWnd,(HMENU)IDC_TOOLBAR,hInst,NULL);
    if(!hWndTB)
    {
        return NULL;
    }
    SendMessage(hWndTB, TB_BUTTONSTRUCTSIZE, (WPARAM)sizeof(TBBUTTON), 0);
    //下面创建三组24x24像素大小的位图图像列表,用于工具栏图标
    hImageList = ImageList_Create(24,24,ILC_COLOR24|ILC_MASK,3,1);
#ifdef PIC_RESOURCE_USED
    hBitmap = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_COLOR3));
#else
    hBitmap = (HBITMAP)LoadImage(NULL, TEXT("color24x3.bmp"), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE|LR_CREATEDIBSECTION); //加载一组图片
#endif
    ImageList_AddMasked(hImageList, hBitmap, RGB(255,255,255));
    DeleteObject (hBitmap);
    SendMessage(hWndTB,TB_SETIMAGELIST,0,(LPARAM)hImageList); //正常显示时的图像列表

    hHotImageList = ImageList_Create(24,24,ILC_COLOR24|ILC_MASK,3,1);
#ifdef PIC_RESOURCE_USED
    hBitmap = LoadBitmap(hInst,MAKEINTRESOURCE(IDB_GREEN3));
#else
    hBitmap = (HBITMAP)LoadImage(NULL, TEXT("green24x3.bmp"), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE|LR_CREATEDIBSECTION);
#endif
    ImageList_AddMasked(hHotImageList,hBitmap, RGB(255,255,255));
    DeleteObject (hBitmap);
    SendMessage(hWndTB,TB_SETHOTIMAGELIST,0,(LPARAM)hHotImageList); //鼠标悬浮时的图像列表

    hDisableImageList = ImageList_Create(24,24,ILC_COLOR24|ILC_MASK,3,1);
#ifdef PIC_RESOURCE_USED
    hBitmap = LoadBitmap(hInst,MAKEINTRESOURCE(IDB_GRAY3));
#else
    hBitmap = (HBITMAP)LoadImage(NULL, TEXT("gray24x3.bmp"), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE|LR_CREATEDIBSECTION);
#endif
    ImageList_AddMasked(hDisableImageList,hBitmap, RGB(255,255,255));
    DeleteObject (hBitmap);
    SendMessage(hWndTB,TB_SETDISABLEDIMAGELIST,0,(LPARAM)hDisableImageList); //当工具栏button失能是的图像列表

    ZeroMemory(tbb, sizeof(tbb));
    tbb[0].iBitmap =MAKELONG(0,0) ;
    tbb[0].fsState = TBSTATE_ENABLED;
    tbb[0].fsStyle = TBSTYLE_BUTTON|BTNS_AUTOSIZE;
    tbb[0].idCommand = ID_FOPEN; 
    tbb[0].iString = (INT_PTR)TEXT("打开");
    tbb[1].iBitmap =MAKELONG(1,0);
    tbb[1].fsState = TBSTATE_ENABLED;
    tbb[1].fsStyle = TBSTYLE_BUTTON|BTNS_AUTOSIZE;
    tbb[1].idCommand = ID_FCLOSE; 
    tbb[1].iString = (INT_PTR)TEXT("关闭");
    tbb[2].iBitmap =MAKELONG(2,0);
    tbb[2].fsState = TBSTATE_ENABLED;
    tbb[2].fsStyle = TBSTYLE_BUTTON|BTNS_AUTOSIZE;
    tbb[2].idCommand = ID_FSAVE;
    tbb[2].iString = (INT_PTR)TEXT("保存");
    SendMessage(hWndTB, TB_ADDBUTTONS, sizeof(tbb)/sizeof(TBBUTTON), (LPARAM)&tbb); //配置工具栏按钮信息
    SendMessage(hWndTB,WM_SIZE,0,0);

    return hWndTB;
}

HWND CreateStatusBar(HWND hParentWnd)
{
#define PANEL_NUM 3
    int array[PANEL_NUM]={120,120*2,-1};
    HINSTANCE hInst = GetModuleHandle(NULL);
    //创建Statusbar控件
    HWND hWndStatus = CreateWindowEx(0, STATUSCLASSNAME, TEXT(""), WS_CHILD|WS_BORDER|WS_VISIBLE, 0, 0, 0, 0, hParentWnd, (HMENU)IDC_STATUSBAR, hInst, NULL);
    if (hWndStatus)
    {
        SendMessage(hWndStatus,SB_SETPARTS,(WPARAM)PANEL_NUM,(LPARAM)array); //设置面板个数
        SendMessage(hWndStatus,SB_SETTEXT,(LPARAM)1,(WPARAM)TEXT("panel-1")); //设置第二个面板内容
        SendMessage(hWndStatus,SB_SETTEXT,(LPARAM)2,(WPARAM)TEXT("panel-2")); //设置第三个面板内容
    }
#undef PANEL_NUM

    return hWndStatus;
}

static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    HDC         hDC;
    PAINTSTRUCT ps;
    static HWND hToolbar;
    static HWND hStatusbar;

    switch (message)
    {
    case WM_CREATE:
        hToolbar = CreateToolbar(hWnd);
        hStatusbar = CreateStatusBar(hWnd);
        return 0;
	
    case WM_COMMAND:
    {
        int wmId    = LOWORD(wParam);
        int wmEvent = HIWORD(wParam);

        // 分析菜单选择:
        switch (wmId)
        {
        case ID_FOPEN:
            SendMessage(hToolbar, TB_ENABLEBUTTON, (WPARAM)ID_FOPEN, (LPARAM)MAKELONG(FALSE,0));
            SendMessage(hToolbar, TB_ENABLEBUTTON, (WPARAM)ID_FSAVE, (LPARAM)MAKELONG(TRUE,0));
            break;
        case ID_FSAVE:
            SendMessage(hToolbar, TB_ENABLEBUTTON, (WPARAM)ID_FSAVE, (LPARAM)MAKELONG(FALSE,0));
            SendMessage(hToolbar, TB_ENABLEBUTTON, (WPARAM)ID_FOPEN, (LPARAM)MAKELONG(TRUE,0));
            break;
        case ID_FCLOSE:
            MessageBox(hWnd, TEXT("click!"), TEXT("hint"), MB_OK);
            break;
        }
    }
    return 0;

    case WM_NOTIFY:
    {
        LPNMHDR lpnmhdr=(LPNMHDR)lParam;
        LPTOOLTIPTEXT lpttext;

        if(lpnmhdr->code==TTN_GETDISPINFO)  
        {
            //处理鼠标在工具栏上悬浮移动时的文本提示
            lpttext=(LPTOOLTIPTEXT)lParam;
            switch(lpttext->hdr.idFrom)
            {
            case ID_FOPEN:
                lpttext->lpszText=TEXT("打开文件");
                break;

            case ID_FCLOSE:
                lpttext->lpszText=TEXT("关闭文件");
                break;

            case ID_FSAVE:
                lpttext->lpszText=TEXT("保存为文件");
                break;
            }
        }
    }
    return 0;

    case WM_SIZE:
    {
        SendMessage(hToolbar, TB_AUTOSIZE, 0, 0);
        SendMessage(hStatusbar, WM_SIZE, 0, 0); 
    }
    return 0;

    case WM_MOUSEMOVE:
    {
        TCHAR szBuf[MAX_PATH];
        _stprintf(szBuf,TEXT("Mouse(%d,%d)"),LOWORD(lParam),HIWORD(lParam));
        SendMessage(hStatusbar, SB_SETTEXT, 0, (LPARAM)(LPSTR)szBuf);
    }
    return 0;

    case WM_PAINT:
        hDC = BeginPaint(hWnd, &ps);
        ;
        EndPaint(hWnd, &ps);
        return 0;

    case WM_DESTROY:
        PostQuitMessage(0);
        return 0 ;
    }
    return DefWindowProc (hWnd, message, wParam, lParam);
}

该实例程序后,结果显示如下图:

该程序的工具栏按下“打开”按钮后,自己后变成Disable状态,点击“保存”后,“打开”按钮会再次激活。可以看到,在鼠标移到“保存”按钮上时,出现提示“保存为文件”的文本提示。

状态栏一共有三个面板,后面两个在创建时静态赋字符串,第一个则实时捕获鼠标在客户区中的坐标位置并显示出来。

总体来说工具栏和状态栏的基本用法还是比较简单,只是由于这两个控件属于微软的通用控件,创建之前需要调用InitCommonControlsEx初始化通用控件库并设置需要使用的控件。本篇就写到这里,感兴趣的读者请继续关注Windows编程基础系列的后续文章。

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

原文发表时间:2015-07-19

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

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏张善友的专栏

JQuery相关资料

将SEO,WEB标准与AJAX进行到底 - JQuery(翻译+学习总结) jQuery——JavaScript冲击波 《15天漫游jQuery》 小试牛刀——...

2168
来自专栏pangguoming

c#以POST方式模拟提交表单

这是我一年前写的一个用C#模拟以POST方式提交表单的代码,现在记录在下面,以免忘记咯。那时候刚学C#~忽忽。。很生疏。。代码看上去也很幼稚 臃肿不堪 #reg...

3469
来自专栏deepcc

Sublime Text 快捷键

3426
来自专栏葡萄城控件技术团队

Xamarin.Forms入门-使用 Xamarin.Forms 来创建跨平台的用户界面

Xamarin.Forms 是一个跨平台的、基于原生控件的UI工具包,开发人员可以轻松的创建适用于 Android,iOS 以及 Windows Phone的用...

4947
来自专栏c#开发者

datagrid资料+ by iCeSnaker - Program rhapsody

datagrid资料+ by iCeSnaker - Program rhapsody 关于datagrid的打印 http://www.chinaaspx.c...

3649
来自专栏林德熙的博客

win10 uwp 改变鼠标 设置光标移动鼠标

经常在应用需要修改光标,显示点击、显示输入,但是有些元素不是系统的,那么如何设置鼠标? 本文主要:UWP 设置光标,UWP 移动鼠标

1511
来自专栏林德熙的博客

WPF 一个性能比较好的 gif 解析库

本文介绍 Magick.NET ,这是 ImageMagick 的 .Net 封装,他支持 100 多种格式的图片,而 gif 也是他支持的。本文告诉大家如何使...

2662
来自专栏我和未来有约会

从Flash到Silverlight进阶教程-用代码来创建动画

从Flash到Silverlight进阶教程 用代码来创建动画 这节里将要讲述一个自定义用户控件最基本的操作,就好象Flash中的MovieClips一样,动态...

3745
来自专栏Golang语言社区

使用Go开发一个简单的服务器程序

最近有个小项目,需要一个简单的后台程序来支撑,本来想用Nodejs来做,但是由于本人js一直很菜,并且很讨厌callback,虽然我也很喜欢异步模型,但我一直都...

3739
来自专栏Golang语言社区

使用Go开发一个简单的服务器程序

最近有个小项目,需要一个简单的后台程序来支撑,本来想用Nodejs来做,但是由于本人js一直很菜,并且很讨厌callback,虽然我也很喜欢异步模型,但我一直都...

3636

扫码关注云+社区

领取腾讯云代金券