• EZSkin——原始框架为构建皮肤功能的应用程序


    介绍 这是一个为MFC应用程序构建可换肤ui的框架。这绝不是完全的,目前只支持基于对话框的应用程序。但是它是高度可扩展的。嗯,一个屏幕截图说明了一千多行代码,其中两行应该更好。 我把整个事情分成三个主题。 的接口 实现和 助手 源代码的注释不是很好。但它是冗长的足以理解和遵循MFC的标准编码规则。我希望这是一项值得的努力。 重要信息——运行演示程序的说明 最初,你将只会得到默认值。当您首先运行它时,列表框中的项。关闭应用程序,然后在注册表中找到HKEY_CURRENT_USERSoftwareEZSuiteEZSkinDemo skin键,并输入提取皮肤的路径作为Dir键的值。 接口 介绍 这是一个简洁的可扩展的架构,以构建Winamp风格的可skinnable应用程序,而不是一个完整的功能库。协议可以分为四层! 皮肤经理→→组件→读者 经理 在示例代码中,CEZSkinManager是执行管理器角色的类。它是一个简单的类,负责一些琐碎的任务,比如从注册表或其他地方加载用户首选项/设置。有四个简单的功能可以帮助我们管理皮肤。 这是一个非平凡类,所有较低的层都是独立于它的。所以,你可以在任何地方,以任何你想要的方式实现它。你甚至可以让你的app类展示这个功能。隐藏,复制Code

    void LoadSkin(CString strSkin);//Loads the skin by name
    //For displaying a Skin browser kind of dialog
    int EnumerateSkins(CStringArray* pstrar);
    virtual void Save();
    virtual void Read();//Registry, Ini or ur own save system

    此外,还有两个助手,他们完全按照自己的建议去做。隐藏,复制Code

    //Makes a path out of a name
    CString GetSkinPath(CString strName,BOOL bValidate =TRUE);
    CString GetCurrentSkinPath() const;

    再举一个难的例子。“管理者做的最少!”: -) 好吧,有一个不那么困难的问题,这个对象应该驻留在哪里。它加载首选项/设置,所以它应该是App类的成员与read &保存在初始化时调用的方法ExitInstance分别。对吧?我只是走了另一条路,从这个和CWinApp一起派生了我的app类。 皮肤 骨干!顾名思义,这就是“皮肤”。CEZSkin表示这个层。 它是一个单元素。它确实是有意义的,因为我无法想象n-skin对象挂在周围,用它们的位图、字体、图标和耗用大量资源。什么不是。此外,它将所有组件结合在一起,并且需要从每个已蒙皮的UI元素中访问,因此最好使用一个带有返回JIT实例的静态函数的单例,而不是使用一个污染了::的全局指针。隐藏,复制Code

    CEZSkin& CEZSkin::Instance()
    {
        static CEZSkin  Instance;//The one and only.
        return Instance;
    }

    组件 这是一个小skinlet。它是特定UI元素或一类UI元素的皮肤。接口IEZComponent表示这一点。隐藏,复制Code

    class IEZSkinComponent : public CObject
    {
    DECLARE_SERIAL(IEZSkinComponent)
    public:
        virtual BOOL Load(IEZSkinIni* pIni,BOOL bLoadDefaultOnFailure  = TRUE) 
        {ASSERT(FALSE); return FALSE;}
        virtual BOOL LoadDefault() 
        {ASSERT(FALSE); return FALSE;}
        virtual void Destroy() 
        {ASSERT(FALSE);}
        virtual BOOL IsLoaded()
        {ASSERT(FALSE); return FALSE;}
        virtual BOOL IsDefault()
        {ASSERT(FALSE); return TRUE;}
    };

    嘿,为什么它是一个愚蠢的assert总是虚函数,而不是一个纯VF?现在终于有了一些辛辣的实施。 不使用抽象类来代替这个pseudo的原因是为了在运行时使用类名创建它。看到DECLARE_SERIAL (IEZSkinComponent)。 我想用这种方式编写代码的原因是这样的。隐藏,复制Code

    CEZSkin& ezs = CEZSkin::Instance();
    ezs.AddComponent(_T("CEZDialogSkin"));
    //class CEZDialogSkin:public IEZSkinComponent

    虽然使用RUNTIME_CLASS的方式完全有可能做到这一点,但我只是认为如果我可以在INI文件/注册表中将类名作为皮肤定义的一部分,这将是很酷的…… CEZSkin类使用CTypedPtrMap保存组件。隐藏,复制Code

    CTypedPtrMap<CMapStringToOb,CString,IEZSkinComponent*> m_mapComponents;

    接口的所有函数都将由CEZSkin调用,它在CEZSkin::GetComponent期间对组件进行JIT实例化。代码读起来是这样的:复制Code

    IEZSkinComponent* pComponent = NULL;
    if(!m_mapComponents.Lookup(strComponent,pComponent))
        return NULL;//Not registered
    
    if(!pComponent)//Not yet created -do JIT Instantiation
    {
        pComponent = 
          (IEZSkinComponent*)CEZRuntimeClass::CreateObject(strComponent);
        ASSERT(pComponent);
        m_mapComponents.SetAt(strComponent,pComponent);
    }
    if(m_bDefault)//Is the default skin loaded
    {
      if(!pComponent->IsDefault()) //Make the component default
      {
       pComponent->Destroy();
       pComponent->LoadDefault();
      }
    }
    else if(!pComponent->IsLoaded())// new?
         pComponent->Load(m_pIni);
    return pComponent;//Ok have it!

    读者 这也是一个伪抽象类,用于提供某些简单的*从皮肤定义读取*函数。隐藏,复制Code

    class IEZSkinIni :public CObject 
    {
    DECLARE_SERIAL(IEZSkinIni)
    public:
      virtual BOOL GetValue(CString strSection,CString strKey,COLORREF& clrValue)
      {ASSERT(FALSE); return FALSE;}//Read Triplet Value
      virtual BOOL GetValue(CString strSection, CString strKey, int& nValue)
      {ASSERT(FALSE); return FALSE;}//Read Integer Value
      virtual BOOL GetValue(CString strSection,CString strKey, CString& strValue)
      {ASSERT(FALSE); return FALSE;}//Read String Value
      virtual BOOL GetValue(CString strSection, CString strKey, CPoint& ptValue)
      {ASSERT(FALSE); return FALSE;}//Read Twin Value
      virtual BOOL Read(CString strCurrentSkinPath)
      {ASSERT(FALSE);return FALSE;}//Init
    };

    工作 步骤1:管理器在读取功能期间加载设置。 在InitInstance期间调用CEZSkinManager::Read()。 第二步:经理用代码向读者介绍皮肤:复制Code

    CEZSkin::Instance().SetIni(_T("CEZSkinIni"));
    //class CEZSkinIni:public IEZSkinIni

    步骤3:管理器加载当前皮肤或设置皮肤为默认。隐藏,复制Code

    void CEZSkinManager::Read()
    {
        m_strSkins = AfxGetApp()->GetProfileString(HKEY_SKINS,HKEY_DIR,_T(""));
        CEZSkin::Instance().SetIni(_T("CEZSkinIni"));
        CFileFind ff;
        BOOL bLoaded = ff.FindFile(m_strSkins);
        if(bLoaded)
        {
          CEZSkin::Instance().SetSkinsDir(m_strSkins);
          m_strCurrentSkin = AfxGetApp()->GetProfileString(HKEY_SKINS,HKEY_SKIN);
          ff.Close();
        }
        LoadSkin(m_strCurrentSkin);
    }
    void CEZSkinManager::LoadSkin(CString strSkin)
    {
        CFileFind ff;
        BOOL bLoaded = ff.FindFile(GetSkinPath(strSkin));
        if(bLoaded)
        {
          m_strCurrentSkin = strSkin;
          bLoaded = CEZSkin::Instance().LoadSkin(m_strCurrentSkin);
        }
        ff.Close();
    }

    步骤4:已蒙皮的对象与CEZSkin通信,以初始化和获取组件。 现在让我们看看与上述任务相关的一些CEZSkin函数。隐藏,复制Code

    virtual void SetIni(CString strClassName);
    virtual void AddComponent(CString strClassName);
    virtual IEZSkinComponent* GetComponent(CString strComponent);
    virtual void LoadDefault();
    virtual BOOL LoadSkin(CString strSkin);

    第一个函数由管理器按上述方式调用。已蒙皮的UI元素(Window)调用下面两个函数,如下所示。隐藏,复制Code

    void CSkinnedWindow::Init()
    {
       //class CMySkin:public IEZSkinComponent
       CEZSkin::Instance().AddComponent(_T("CMySkin"));
       ....
    }
    void CSkinnedWindow::OnPaint()
    {
        CPaintDC dc(this);
        CEZSkin& skin = CEZSkin::Instance();
        CMySkin* pSkin = skin.GetComponent(_T("CMySkin"));
        //////Do Painting by getting the attributes of the component
        //say..
        COLORREF clrBack = pSkin->GetBackgroundColor();
        dc.FillSolidRect(CEZClientRect(this),clrBack);
        .....
    }

    步骤5:最后,管理器将当前设置写入存储。 在ExitInstance期间调用CEZSkinManager::Save。隐藏,复制Code

    AfxGetApp()->WriteProfileString(HKEY_SKINS,HKEY_DIR,m_strSkins);
    AfxGetApp()->WriteProfileString(HKEY_SKINS,HKEY_SKIN,m_strCurrentSkin);

    实现 介绍 在演示中,我实现了EZSkin界面来创建一个带皮肤的对话框。严格地说,这里应该讨论CEZSkinManager,但是如果没有这个接口,我就很难解释这个接口s类。 下面的类构成了这个实现的基础。 CEZSkinIni 这提供了IEZSkinIni的默认实现。它将阅读器层实现为INI文件。我使用了Iuri Apollonio的CIni类,并修改了它以适应框架。 它使用一个CStdioFile来读取INI文件,并将每一行存储在一个CStringArray中,然后解析每一行以获得所需的值。我用了a;作为注释启动器,作为值分隔符。 它使用AfxExtractSubString来解析逗号分隔的值。 样本皮肤INI; 隐藏,复制代码(皮肤) Name =黑色; 作者= V。拉克希米纳史木汗; 注释=黑骏马; (主要) Bmp = back.bmp; 画=瓷砖; (标题) Bmp = Caption.bmp; 画=瓷砖; TextFont = ARial Black,B,25; 输入TextColor = 200200200; BtnsNormal = btns.bmp; BtnsHilight = btnsh.bmp; TransColor = 192224, 64; BtnPos = 7, 27岁,47岁; BtnWidth = 20; CEZGenericSkin 这提供了IEZSkinComponent的默认实现,并且仍然是伪抽象,拥有一些assert always函数。 这个类为需要以下皮肤属性的窗口提供了接口: 背景位图, 背景颜色, 文本颜色, 文本字体 这个类使用以下成员保存数据:复制Code

    BOOL m_bDefault;
    BOOL m_bLoaded;
    CEZDib m_Dib;//See the helpers section
    CFont m_font;
    COLORREF m_clrTxt;
    COLORREF m_clrBk;

    要使用这个类,我们应该从这个派生并覆盖下面的函数: 隐藏,复制代码//{伪纯虚函数 因为虚拟字符串GetSection () {断言(假),返回_T (" ");} 虚拟空间LoadDefaultBmp(){断言(假);} 虚拟空间LoadDefaultFont(){断言(假);} 虚拟空间LoadDefaultBackColor(){断言(假);} 虚拟空间LoadDefaultTextColor(){断言(假);} / /} 它为IEZSkinComponent接口公开的所有函数提供了默认实现。派生类必须重写上述函数的原因是:复制Code

    BOOL CEZGenericSkin::LoadDefault()
    {
        LoadDefaultBmp();
        LoadDefaultBackColor();
        LoadDefaultTextColor();
        LoadDefaultFont();
    
        m_bDefault = TRUE;
        m_bLoaded = TRUE;
        return TRUE;
    }

    它也有一个很酷的助手,加载字体到m_font成员给定的字体名称样式和宽度。隐藏,复制Code

    BOOL CEZGenericSkin::LoadFont(CString strFont, CString strStyle, int nHeight)

    如使用方法:隐藏,复制Code

    LoadFont(_T("Times New Roman"),_T("BI"),20);

    要了解使用CEZGenericSkin实现IEZSkinComponent有多容易,请查看CEZDialogSkin的定义。 CEZDialogSkinHide,复制Code

    IMPLEMENT_SERIAL(CEZDialogSkin,IEZSkinComponent,(UINT)-1)
    
    CString CEZDialogSkin::GetSection()
    {return _T("Main");}
    
    void CEZDialogSkin::LoadDefaultBackColor()
    {m_clrBk= RGB(0,0,255);}
    
    void CEZDialogSkin::LoadDefaultBmp()
    {
        m_Dib.Load(IDB_BACK);
        m_Dib.SetType(CEZDib::BMP_TILE);
    }
    
    void CEZDialogSkin::LoadDefaultFont()
    {LoadFont(_T("Times New Roman"),_T("B"),20);}
    
    void CEZDialogSkin::LoadDefaultTextColor()
    {m_clrTxt= RGB(255,0,0);}

    CEZCaptionSkin 它没有CEZDialogSkin那么小。 它有额外的成员为标题按钮- Rects,突出显示&;正常位图和透明颜色的位图。隐藏,复制Code

    CEZDib m_DibBtnNormal;
    CEZDib m_DibBtnHilight;
    CRect m_rectBtns[3];
    COLORREF m_clrTransparent;

    助手 介绍 这里我们只看一下在演示中使用的各种helper类。 矩形 这些是来自CRect的类,它们封装了CWnd::GetxxxRect函数和CDC::GetClipBox,这样就可以编写如下代码:Hide复制Code

    CPaintDC dc(this);
    
    //CEZDib dib;
    dib.Draw(&dc,CEZClientRect(this));
    
    //instead of 
    //CRect rect;
    //GetClientRect(&rect);
    //dib.Draw(&dc,rect);

    DCs CEZMemDC,是带有附加bCopyOnDestruct参数的CMemDC,该参数阻止DC将其内容传输到目标。CEZBmpDC选择一个位图或它的一部分到一个兼容的DC,并可以用作一个刮板。 最酷的一个是CEZMonoDC,它接收一个DC并创建一个带有源DC的单色位图的DC。隐藏,复制Code

    CEZMonoDC(CDC* pDCSrc,LPRECT pRect=NULL):CDC()
    {
        ASSERT(pDCSrc != NULL);
        CreateCompatibleDC(pDCSrc);
        m_rect = pRect?*pRect:CEZClipRect(pDCSrc);
        m_bitmap.CreateBitmap(m_rect.Width(),m_rect.Height(),1,1,NULL);
        pDCSrc->SetBkColor(pDCSrc->GetPixel(  0, 0 ) ) ;
        m_pOldBitmap =(CBitmap*)SelectObject(&m_bitmap);
        SetWindowOrg(m_rect.left, m_rect.top);
    }

    CEZDib 这是建立在Jorg Konig的CDIBitmap类。我已经包括了我发现的其他DIB类的某些好东西。我对CDIBitmap所做的重要改变是,按照Paul DiLascia在期刊97中建议的,使它可以作为CBitmap通过。我还添加了四个绘图函数来绘制一个普通的位图,拉伸的位图,平铺的位图和一个透明的绘制。隐藏,收缩,复制Code

    BOOL CEZDib::DrawTransparent(CDC* pDC,COLORREF clrTrans, 
      const CRect& rcDest,const CRect& rcSrc) const
    {
        CRect rcDC(rcDest),rcBmp(rcSrc);
    
        if(rcDC.IsRectNull()) rcDC =CEZClipRect(pDC);
        if(rcBmp.IsRectNull()) rcBmp = CRect(0,0,GetWidth(),GetHeight());
    
    
        CEZMemDC memDC(pDC,&rcDC,TRUE,TRUE ),imageDC(pDC,&rcDC,FALSE);
        CEZMonoDC backDC(pDC,&rcDC),maskDC(pDC,&rcDC);
    
        DrawNormal(&imageDC,rcDC,rcBmp);
    
        COLORREF clrImageOld = imageDC.SetBkColor(clrTrans);
        maskDC.BitBlt(rcDC.left,rcDC.top,rcDC.Width(),
          rcDC.Height(),&imageDC,rcDC.left,rcDC.top,SRCCOPY);
        imageDC.SetBkColor(clrImageOld);
    
        backDC.BitBlt(rcDC.left,rcDC.top,rcDC.Width(),
          rcDC.Height(),&maskDC,rcDC.left,rcDC.top,NOTSRCCOPY);
    
        memDC.BitBlt(rcDC.left,rcDC.top,rcDC.Width(),rcDC.Height(),
          &maskDC,rcDC.left,rcDC.top,SRCAND);
    
        imageDC.BitBlt(rcDC.left,rcDC.top,rcDC.Width(),
          rcDC.Height(),&backDC,rcDC.left,rcDC.top,SRCAND);
    
        memDC.BitBlt(rcDC.left,rcDC.top,rcDC.Width(),rcDC.Height(),
          &imageDC,rcDC.left,rcDC.top,SRCPAINT);
    
        return TRUE;
    }

    CEZWindowNC 封装CWnd的非客户区域函数的类。隐藏,复制Code

    BOOL HasBorder();
    BOOL HasSysMenu();
    BOOL HasCaption();
    CRect GetCaptionRect();
    CRect GetLeftBorderRect();
    CRect GetRightBorderRect();
    CRect GetTopBorderRect();
    CRect GetBottomBorderRect();

    CEZDialog 这是样例蒙皮UI元素。隐藏,复制Code

    BOOL CEZDialog::OnEraseBkgnd(CDC* pDC) 
    {
        CEZSkin& ezs = CEZSkin::Instance();
        CEZDialogSkin* pSkin  = 
          DYNAMIC_DOWNCAST(CEZDialogSkin,
          ezs.GetComponent(_T("CEZDialogSkin")));
        ASSERT(pSkin);
        const CEZDib& bmp = pSkin->GetBackgroundBitmap();
        CEZClientRect rcClient(this);
        bmp.Draw(pDC,rcClient);
        return TRUE; 
    }
    void CEZDialog::Init()
    {
        CEZSkin& ezs = CEZSkin::Instance();
        ezs.AddComponent(_T("CEZDialogSkin"));
        VERIFY(m_brushHollow.CreateStockObject(HOLLOW_BRUSH));
    }

    哇,这段代码对于带皮肤位图背景的对话框来说是不是太小了? CEZCaption 我已经基于Dave Lorde的CCaption代码创建了这个类。我修改了原始代码以使用CEZSkin,还添加了用于绘制和处理标题按钮的代码。它广泛使用CEZDib和CEZWindowNC。我还做了一些修改,使它在对话框中工作。 尽管标题可以很好地描绘和处理按钮,但我在鼠标跟踪方面遇到了一些问题。我以减少功能为代价简化了对类的跟踪。如果有人写一篇关于如何做到这一点的文章,那就太好了。 更新 2001年1月30日 固定静态库崩溃。 标题中鼠标跟踪的不一致。 添加一个CEZBorder类来绘制边框。 本文转载于:http://www.diyabc.com/frontweb/news12297.html

  • 相关阅读:
    从Java角度理解Angular之入门篇:npm, yarn, Angular CLI
    大数据开发实战:Stream SQL实时开发一
    大数据开发实战:Spark Streaming流计算开发
    大数据开发实战:Storm流计算开发
    大数据开发实战:Hadoop数据仓库开发实战
    数据仓库中的拉链表
    java通过jdbc连接impala
    impala-shell常用命令
    Kudu-java数据库简单操作
    拉链表--实现、更新及回滚的具体实现( 转载)
  • 原文地址:https://www.cnblogs.com/Dincat/p/13473899.html
Copyright © 2020-2023  润新知