zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    关于这个 blog
    P6499 [COCI2016-2017#2] Burza 题解
    CF1172F Nauuo and Bug 题解
    CF1479D Odd Mineral Resource 题解
    CF1442E Black, White and Grey Tree 题解
    CF1442D Sum 题解
    CF1025D Recovering BST 题解
    CF1056E Check Transcription 题解
    CF1025F Disjoint Triangles 题解
    红包算法的PHP实现
  • 原文地址:https://www.cnblogs.com/Dincat/p/13473899.html
Copyright © 2011-2022 走看看