rt

解决方案 »

  1.   

    原理:
      如果能在自己的程序中也具备动态更换皮肤的能力,将使软件平添几分亮点从而更易博得用户的青睐。具笔者了解,当前介绍这种技术的文章资料较少,与其流行程度很不协调,而且现有的少量资料也多是针对对话框进行的,无非是在对话框上覆盖一层图片,并通过更换图片来实现程序的动态换肤。虽然这也是动态换肤的一种,但由于技术过于简单,本文将着重介绍另外一种动态换肤技术--工具栏的动态换肤技术,这也是IE浏览器等软件所普遍采用的一种换肤技术。  程序支持动态换肤  由于换肤功能是对程序框架的扩展,所以相关处理代码理所当然地要在主框架类中进行。为了使程序能具备换肤的能力,必须首先使程序具备换肤的基本条件。首先可以明确的是:处理对象为普通的工具条,其基类为CToolBar。通过MSDN帮助可以了解到该类及其"近亲"的继承关系如图所示。通过对这几个相关类的查阅,可以发现CReBar类比较特殊,它本身并不用于显示,而主要用于包容其他的工具条,例如,可以通过其成员函数AddBar()将工具条(ToolBar)、对话条(DialogBar)等类型的工具条加入到复合条(ReBar),使工具栏中可以显示多个工具条。所以可以得出这样的设计思路:程序框架的工具栏不再直接以工具条来实现,而是以复合条为基础,并通过CReBar的AddBar()函数将原先的工具条加入其中。皮肤则可以通过设置与CReBar相关的REBARBANDINFO结构而平铺于复合条之上,由于有工具条覆于其上,因此必须在创建工具条时修改其风格(Style),使其背景为透明,这样才能透出位于下层的皮肤。
       由于在程序设计时使用了CReBar,在普通程序中实现起来比较麻烦,可以在创建工程时在第四步选中"Internet Explorer ReBars"选项,这样创建的工具栏将在复合条上添加一个工具条和一个对话条。只需从主框架类的头文件和实现文件的OnCreate()函数里将对话条CDialogBar的实例对象m_wndDlgBar的声明部分和部分相关代码去除即可。  接下来在工具条创建时需要特别指定TBSTYLE_TRANSPARENT等窗口风格来使其背景透明,并在创建完复合条后通过AddBar()函数将背景透明的工具条添加其中:m_wndToolBar.CreateEx(this,
    TBSTYLE_FLAT|TBSTYLE_TRANSPARENT,
    WS_CHILD|WS_VISIBLE|CBRS_ALIGN_TOP,
    CRect(0,0,0,0),AFX_IDW_TOOLBAR)
    ……
    m_wndReBar.AddBar(&m_wndToolBar,
    NULL,NULL,
    RBBS_GRIPPERALWAYS|RBBS_FIXEDBMP|RBBS_BREAK)   经过上述几步处理对程序的主框架作了改造,使其基本具备了动态换肤的必要条件。  皮肤的装载与动态更换  皮肤一般是以外部资源的形式发布,在使用时再由程序动态装载。皮肤加载到工具栏的过程大致可分两步:先将皮肤从文件装载到内存,然后再由内存位图直接绘制到复合条上去。对于第一步,可用API函数LoadImage将外部文件back.bmp从文件按位图的格式装载到内存,返回的句柄可通过强制转换得到HBITMAP型的位图句柄m_bmpBack:m_bmpBack=(HBITMAP)LoadImage(AfxGetInstanceHandle(), //应用程序实例句柄
    "back.bmp",IMAGE_BITMAP,0,0,
    LR_LOADFROMFILE|LR_CREATEDIBSECTION);   皮肤装载到内存后,需要通过对REBARBANDINFO结构进行设置,才能实现皮肤位图在复合条中的平铺,该结构有二十多个成员变量,但在此只是用于设置背景位图的平铺,因此只需设置fMask为RBBIM_BACKGROUND以指定hbmBack有效,并将前面装载到内存的位图句柄m_bmpBack传递给该成员变量就完成了对皮肤的装载与显示,下面就是这部分实现的详细代码: CReBarCtrl& rc=m_wndReBar.GetReBarCtrl(); //获取复合条控件指针
    REBARBANDINFO info;
    memset(&info,0,sizeof(REBARBANDINFO)); //清零
    info.cbSize=sizeof(info);
    info.fMask=RBBIM_BACKGROUND; //指定hbmBack有效
    //如果位图句柄不可用则仍为银灰色背景,否则以此位图作为复合条的背景
    info.hbmBack=m_bmpBack!=INVALID_HANDLE_VALUE?m_bmpBack:NULL;
    rc.SetBandInfo(0,&info); //进行设置
    rc.UpdateWindow(); //更新窗口   当皮肤已经显示到工具栏上而对其进行动态更换,则只需要简单的调用拷贝文件函数将新的皮肤插件以覆盖的形式复制到back.bmp,并再次调用上述两步对新的皮肤插件进行重新装载即可实现对程序的动态皮肤更换。拷贝文件函数一般用API函数CopyFile()来实现,该函数的前两个参数分别为源文件路径和目的文件路径。最后一个布尔型参数指定文件拷贝的方式,在此需要指定为FALSE,即如果目的文件已存在则对其覆盖,否则将无法实现对皮肤的更换。例子:http://www.vckbase.com/sourcecode/advancedui/SkinDemo.zip