界面换肤软件学习笔记

动态库实现界面换肤

一:动态载入DLL并获取DLL中函数

//在全局区创建一个函数指针
typedef void (_stdcall* funShowDlg)();

//获取动态库,LoadLibrary为动态库文件的存放位置
    HMODULE hMod=LoadLibrary("../SkinDll/Debug/SkinDll.dll");
    funShowDlg ShowDlg;
    if(hMod)
    {
        //获取动态链接库中ShowDlg函数的地址
        ShowDlg=(funShowDlg)GetProcAddress(hMod,_T("ShowDlg"));
        if(ShowDlg)
            ShowDlg();
        FreeLibrary(hMod);
    }

二:在动态链接库中载入位图

//AfxGetResourceHandle用于获取当前资源模块句柄,
//而AfxSetResourceHandle则用于设置程序目前要使用的资源模块句柄。
//为了程序能够访问动态链接库中的位图资源,动态库需要使用静态连接MFC

HBITMAP GetBmpResourceFromDll()
{
    return LoadBitmap(AfxGetResourceHandle,MAKEINTRESOURCE(IDB_BACKGROUND));
    //或者使用以下这句
    //  m_pParts[i].m_hBitmap = //LoadBitmap(AfxGetInstanceHandle(),MAKEINTRESOURCE(1000+i));
}

处理窗口的WM_CTLCOLOR消息,如果已经加载皮肤文件,则从皮肤文件中加载窗口背景位图,作为窗口的背景位图

CWnd::OnCtlColor
afx_msg HBRUSH OnCtlColor( CDC* pDC, CWnd* pWnd, UINT nCtlColor );
//返回值:OnCtlColor必须返回一个刷子句柄,该刷子将被用于画出控件的背景。

三:动态库使用
在使用动态库方式加载皮肤文件时候,在主应用程序中如果需要用到DLL中的某个函数,则主应用程序应当存在一个与动态库中需要使用的函数同名的一个纯虚函数(注意主应用程序中是纯虚函数,DLL中的同名函数是虚函数),除此之外,主应用程序中的所有纯虚函数的排列顺序必须与DLL中的虚函数的顺序一致(目的是使虚函数表一致),如果虚函数表不一致,在主应用程序中调用DLL中的函数的时候就会调用到错误的函数甚至访问到不可访问的空间而使程序当掉,有一点还需要注意,虚析构函数一会存在与虚函数表中,也需要注意它的排列顺序,非虚函数和变量则无需考虑顺序,实例如下

//主应用程序里的类
class CSkinManage  
{
protected:
    UINT m_PartCount;    //窗体由几部分组成
    /******************窗体各部分位图资源********************
    0,1,2           分别为标题栏的左\中\右3部分
    3,4,5           分别为左,下,右边框
    6,7,8,9,10,11   为标题栏普通按钮和热点按钮
    *********************************************************/
    CFormPart* m_pParts;


public:
    //加载位图资源 
    virtual HBITMAP GetBitmapRes(UINT Index) = 0;
    virtual Release() = 0;
    virtual CPoint GetButtonPos(UINT Index) = 0;
    virtual BOOL GetDrawRound() = 0;
    virtual COLORREF GetMenuBKColor() = 0;
    virtual COLORREF GetMenuSelColor() = 0;

public:
    CSkinManage();
    virtual ~CSkinManage();
};


//DLL中的对应类
class CSkin
{
protected:
    UINT m_PartCount;  //窗体由几部分组成
    /******************窗体各部分位图资源索引: *******************

    0,1,2:         分别为标题栏的左\中\右3部分
    3,4,5:         分别为左,下,右边框
    6,7,8,9,10,11: 为标题栏普通按钮和热点按钮
    12,13:         表示左下角和右下角位图
    14:            表示背景位图
    *************************************************************/
    CFormPart* m_pParts;
    COLORREF m_MenuBkColor;     //菜单背景颜色
    COLORREF m_MenuSelColor;    //菜单选中时的颜色
    BOOL DrawRound;             //是否绘制圆角

public:


    //获取位图资源
    virtual HBITMAP GetBitmapRes(UINT Index)
    {
        return m_pParts[Index].m_hBitmap;
    }
    //释放对象
    virtual Release()
    {   
        delete this;
    }
    //获取按钮的位置
    virtual CPoint GetButtonPos(UINT Index)
    {
        return  m_pParts[Index].m_Pos;
    }

    virtual BOOL GetDrawRound()
    {
        return DrawRound;
    }
    virtual COLORREF GetMenuBKColor()
    {
        return m_MenuBkColor;
    }
    virtual COLORREF GetMenuSelColor()
    {
        return m_MenuSelColor;
    }

    void LoadBitmapRes();       //加载位图资源
    void SetButtonPos() ;       //设置标题栏按钮的相对位置
public:
    CSkin();
    CSkin(UINT PartCount);
    ~CSkin();

};

如果把virtual ~CSkinManage();这一句移动到 virtual HBITMAP GetBitmapRes(UINT Index) = 0;这一句前面,整个程序就会当掉,因为你在调用GetBitmapRes(UINT Index)这个函数的时候其实调用的是~CSkinManage()这个函数,结果显然意见

四:菜单默认位置
当窗口Style属性为Resizing时,菜单的左边界肯定为4(在不人为改变菜单位置的情况下);
当窗口Style属性为Dialog Frame时,菜单的左边界肯定为3(在不人为改变菜单位置的情况下)

五:自绘响应函数
OnMeasureItem(..)函数用于设置自绘项的尺寸
OnDrawItem(…)函数用于根据前面得到的尺寸绘制需要自绘的项目

六:消息响应函数OnSysCommand(OnSysCommand(UINT nID, LPARAM lParam)

void CMynetsendDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialog::OnSysCommand(nID, lParam);

解释:对话框的系统菜单命令,包括关闭对话框,最小化最大化,弹出关于对话框等等,实际上就是向这个对话框发送WM_SYSCOMMAND消息,对话框响应WM_SYSCOMMAND消息,然后根据不同的nID值判断到底是什么系统命令(关闭对话框,最大最小化还是其他什么),这个OnSysCommand
就是用来响应WM_SYSCOMMAND消息,进行相应处理的。
上面的if ((nID & 0xFFF0) == IDM_ABOUTBOX),就是说在需要弹出关于对话框的时候,就进行特别处理(因为在MFC自动生成的基于对话框的工程里面,这个关于对话框也是用户自己可以控制的),所以这时候就自己处理,弹出自己的关于对话框,对于其他的消息,默认不需要用户定制,就直接调用父类的处理就好

七:OnNcCalcSize()消息响应函数
功能:OnNcCalcSize改变标题栏等的高度
在创建窗口时,当收到 WM_NCCALCSIZE 消息时才指定客户区。不管什么时候,只要 Windows 想知道窗口客户区的大小,它便会发送这个消息。
NCCALCSIZE_PARAMS 结构保存三个矩形数组,第一个保存窗口的客户区。
如果改写主窗口的 WM_NCCALCSIZE/OnNcCalcSize,一定要确保调用基类的默认窗口处理例程,以便实现缺省处理。这样程序一运行便会有得到默认的客户区矩形,然后你可以调整其大小。同样,还应该在OnNcPaint/WM_NCPAINT 中调用基类默认的处理过程。否则 Windows 不会绘制边界,滚动栏或其它标准非客户区元素。如果你实现自己的窗口类,像定制工具栏或调色板,其中要计算客户区矩形并进行绘制处理,你可以不必调用基类默认的窗口过程。随便哪种方法,当窗口收到 WM_NCPAINT 消息时,你都得负责绘制整个非客户区。

八:消息响应函数CalcWindowRect(LPRECT lpClientRect, UINT nAdjustType)
每当主框架窗口的客户区尺寸发生变化或控制条的位置发生变化,需要重新排列客户区时,调用该函数,根据视图客户区尺寸计算视图窗口的尺寸。
我们知道,排列主窗口客户区是由CFrameWnd::RecalcLayout()完成的。显然,视图的CalcWindowRect()函数也是由它触发调用的。主窗口的客户区尺寸减掉所有控制占用的部分,剩下的区域分给视图,这部分区域作为实参传入CalcWindowRect()。在CalcWindowRect()函数内,需要计算视图窗口的尺寸。代码如下:

void CView::CalcWindowRect(LPRECT lpClientRect, UNIT nAdjustType)
  {
  // lpClientRect此时是整个视图客户区的尺寸
  // 需要为滚动条增加尺寸吗
  if (nAdjustType != 0)
  {
  // 调用API,根据窗口风格计算窗口尺寸
  ::AdjustWindowRectEx(lpClientRect, 0, FALSE, GetExStyle());
  DWORD dwStyle = GetStyle();
  if (dwStyle & WS_VSCROLL)
  {
      // 为垂直滚动条增加尺寸
      int nAdjust = afxData.csVScroll;
      if (dwStyle & WS_BORDER)
      nAdjust -= CX_BORDER;
      lpClientRect->right += nAdjust;
  }
  if (dwStyle & WS_HSCROLL)
  {
      // 为水平滚动条增加尺寸
      int nAdjust = afxData.cyHScroll;
      if (dwStyle & WS_BORDER)
      nAdjust -= CY_BORDER;
      lpClientRect->bottom += nAdjust;

      }
        return;
  }

  // 无需为滚动条增加尺寸,调用基类成员完成计算
  CWnd::CalcWindowRect(lpClientRect, nAdjustType);
  }

组件库实现界面换肤
一:OnCtrlColor(HWND hWnd)
如果在某个函数中使用了CWindowDC或者CClientdc这两种画图设备,则无需人工释放(xxx->DeleteDC()),这连个类的析构函数会自动释放创建的画图设备,如果人工释放,会因重复释放而使程序当掉

二:WM_CTRCOLOR
在组件库中需要自己修改窗口过程,然后在窗口过程中有些时候需要使用到WM_CtrlColor消息,笔者在使用的过程中发现单纯的使用WM_CtrlColor消息,则自定义窗口过程函数不会响应这个消息,需要使用WM_CtrlColorDlg或者WM_CtrlColorEdit或者WM_CtrlColorButton,因为笔者在组件库中分别为三种对象修改了窗口过程,单纯使用WM_CtrlColor不会被响应,需要加上类别

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 开源MFC界面换肤文件是一种可以自定义MFC界面外观的文件。MFC(Microsoft Foundation Class)是微软公司开发的一套用于Windows应用程序的类库,它提供了许多界面组件及辅助工具,用于快速开发Windows应用程序。 开源MFC界面换肤文件是由开源社区或个人开发者提供的允许用户自由使用、修改和分发的文件。它包含了改变MFC界面外观的相关资源文件,如图标、背景、按钮样式等。用户可以根据自己的需求和喜好,选择合适的皮肤文件来自定义MFC界面,使其符合自己的视觉风格。 使用开源MFC界面换肤文件可以带来以下几个优点: 1. 外观定制化:用户可以自由选择合适的皮肤文件,将MFC界面外观与自己的品牌形象或个人喜好相匹配,提升用户体验。 2. 界面变换:通过更换皮肤文件,可以随时改变MFC界面的外观,给用户带来新鲜感和活力,提高用户的工作效率和兴趣。 3. 提高可扩展性:开源MFC界面换肤文件可以作为一个外部组件,可以与其他功能模块和插件相集成,提供更丰富的界面定制化选项。 4. 社区支持:由于是开源文件,用户可以与开源社区互动,分享和获取更多的皮肤文件和改进建议,从而得到更好的支持和协作。 总之,开源MFC界面换肤文件为MFC开发者提供了更多的界面外观定制化选项,可以根据自己的需求和喜好来优化和创新MFC界面,提升用户体验和工作效率。同时,也在开源社区中形成了一种互动和分享的开发文化。 ### 回答2: 开源MFC界面换肤文件是一种允许用户自定义界面外观和主题的文件。它可以让开发人员在MFC应用程序中实现界面换肤功能。 开源的换肤文件通常是由MFC开发者共享的、可自由使用和修改的代码。它们通过使用不同的皮肤和主题文件,可以改变应用程序的外观,包括按钮样式、颜色、字体等。 在使用开源的换肤文件时,我们需要将相应的文件引入到MFC项目中,并根据自己的需求进行配置和定制。一般来说,开源的换肤文件会提供一些示例代码和文档,以帮助开发者快速上手。 通过使用开源的换肤文件,我们可以实现应用程序在不同操作系统和平台上具有一致的外观,提升用户体验。同时,开源的换肤文件也为开发者提供了一个更加灵活和易于维护的界面定制方案。 开源的换肤文件在MFC开发中的应用十分广泛,因为它们可以大大减少编写自定义界面代码的工作量,提高开发效率。此外,通过开源共享,开发者还可以从其他人的经验和技巧中获取灵感和帮助,更好地应对界面换肤的挑战。 总结而言,开源MFC界面换肤文件是一种允许用户自定义界面外观和主题的开发工具。通过使用开源的换肤文件,我们可以轻松地实现界面换肤功能,提高应用程序的用户体验和开发效率。 ### 回答3: 开源的MFC界面换肤文件是一种用于改变MFC应用程序界面样式的文件,它可以实现界面的个性化定制和美化。这种文件通常使用XML或INI等格式保存,其中包含了界面元素的布局信息、颜色样式、图标等。开源的MFC界面换肤文件具有以下几个特点: 首先,开源的MFC界面换肤文件提供了丰富多样的主题和皮肤选择,用户可以根据自己的喜好和需求来选择适合的皮肤文件,使应用程序界面焕然一新。 其次,开源的MFC界面换肤文件具有易于使用和集成的特点。用户只需要将皮肤文件导入应用程序中,并在代码中调用相应的接口进行界面切换,便可实现换肤功能。 此外,开源的MFC界面换肤文件还提供了扩展性和自定义性。用户可以根据自己的需求对皮肤文件进行修改和定制,添加自定义的界面元素和效果,使应用程序界面更加个性化。 最后,开源的MFC界面换肤文件通常具有良好的兼容性和稳定性。它们经过多次测试和优化,可以在各种Windows操作系统和MFC版本下正常运行,不会对应用程序的性能和稳定性产生负面影响。 总之,开源的MFC界面换肤文件为开发者提供了一种简单、灵活和美观的界面定制方案,可以大大提升应用程序的用户体验和吸引力。同时,开源的特性还使得这些文件易于集成和定制,满足了不同用户的多样化需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值