“剥皮”UI控件库(vc++)

介绍 这个库承诺通过利用图像、GDI、合成和多重继承的强大功能,为那些想开发定制UI (curves等人)的用户提供非windows UI外观(只做了一点小小的修改——使其非常通用)。 灵感来自…… 几年前,当我第一次在Winamp (MP3播放器)上看到很酷的皮肤时,我感到很兴奋,并面临着编写一个用于未来开发的库的挑战,这会让那些认为漂亮的UI只在web应用程序和Flash应用程序中可行的人们感到震惊! 图书馆里面有什么? 该库由以下类组成: 所有控件的父类,包含公共的functionalityCSkinnedStatic自定义类作为静态控件或标签 继承自:Cwnd, CSkinControl 作为按钮控件的自定义类 继承自:Cwnd, CSkinControl 自定义类作为编辑控件 继承自:Cwnd, CSkinControl CSkinnedComboBox -自定义类作为组合框控件 继承自:Cwnd, CSkinControl 由:CSkinnedEdit, CSkinnedButton和CSkinnedListBox组成 自定义类作为一个列表框控件 继承自:Cwnd, CSkinControl 组成:CSkinnedButton CSkinnedScrollBar -自定义类,用作滚动条控件 继承:Cwnd, cskincontrol组成:CSkinnedButton 自定义类作为滑块控件 继承:Cwnd, cskincontrol组成:CSkinnedButton 建筑上的细节…… 其思想是将尽可能多的常用功能存储在一个类(CSkinControl)中,然后通过继承在具体的控制类中使用它。基类包含对四个不同图像(id)的引用,一个用于正常状态,一个用于禁用状态,一个用于悬停状态,还有一个用于按下状态。存储它的函数是SetImageResources(正常、悬停、按下、禁用)。基类还包含以下功能: 位置和尺寸: 顶部SetCoordinates(左)SetDimensions(宽度、高度)GetLeft () GetTop () GetWidth()获得() 颜色和字体: GetTextColor GetCurrentBackgroundColor () () GetBackgroundColor(状态)SetBackgroundColor(状态、颜色)SetForegroundColor(颜色)SetTextColor(颜色)SetFontName(名字)SetFontStyle(风格)SetFontSize(大小)GetFontName () GetFontStyle () GetFontSize () 最重要的函数是UpdateMemoryDC(),它负责绘制和更新屏幕上每个控件的视觉效果,无论该控件处于默认状态还是由某些用户操作(鼠标事件)触发。 隐藏,收缩,复制Code

// This function attempts to load image
// resources from a DLL and renders the same on the screen

int CSkinControl::UpdateMemoryDC()
{
    HBITMAP hBitmap = NULL;
    BITMAP bmpTemp;
// If gifs are the preferred resources, use conversion
#ifdef USE_GIF_IMAGES
    hBitmap = LoadGIF(GetDllInstance((LPCTSTR)m_csDLLFileName),
                      MAKEINTRESOURCE(GetID()));
#else
    hBitmap = LoadBitmap(GetDllInstance((LPTSTR)(LPCTSTR)m_csDLLFileName), 
                         MAKEINTRESOURCE(GetID()));
#endif
    if(hBitmap != NULL)
    {
        ::GetObject(hBitmap, sizeof(BITMAP), &bmpTemp);
        m_lImageWidth = bmpTemp.bmWidth;
        m_lImageHeight = bmpTemp.bmHeight;
        ::SelectObject(m_dcMemory.GetSafeHdc(),hBitmap);
    }
    // If the object is of text type (edit)
    else if(m_nPressedID == -1 && m_nUnPressedID == -1 && m_nHoverID == -1)
    {        
        m_dcMemory.SetTextColor(m_crTextColor);
        m_dcMemory.DrawText(m_csText, CRect(0, 0, m_nWidth, m_nHeight), DT_CENTER);
    }
    return 0;
}

具体类提供了它们的标准对等物所需要的功能。例如,CSkinnedEdit支持文本选择,插入,删除(没有实现复制粘贴-抱歉!!),以及其他定制功能,如“只读”,“小数点验证”等。类似地,CSkinnedScrollBar提供了设置最小范围、最大范围、检索滚动条按钮位置等功能。代码和函数名是不言自明的。我很抱歉没有提供很多内联代码注释,你可以随时联系我。 所有控件都是动态创建的。它们每个都有一个CreateSkinControl函数(名称、rect、父类、id、标志),它接受前面提到的参数。最后一个(标志)是一个有趣的参数,它包含创建所需的任何“额外”信息(您将在不同的控件中看到)。例如,下面显示的是CSkinnedButton控件的创建代码: 隐藏,收缩,复制Code

BOOL CSkinnedButton::CreateSkinControl(LPCTSTR lpszWindowName, LPRECT lpRect, 
                     CWnd *pParentWnd, UINT nControlID, long lFlags)
{
    // Set windows name, location, size, parent, and control id
    m_csText = lpszWindowName;
    m_nLeft = lpRect->left;
    m_nTop = lpRect->top;
    m_nWidth = lpRect->right - lpRect->left;
    m_nHeight = lpRect->bottom - lpRect->top;
    m_pParentWnd = pParentWnd;
    m_nControlID = nControlID;
    
    // Assign a default font and defaut colors
    m_csFontName = "Arial";
    m_nFontSize = 16;
    m_nFontStyle = FONT_NORMAL;
    m_crBackgroundColorHover = RGB(255,255,255);
    m_crBackgroundColorPressed = RGB(255,255,255);
    m_crBackgroundColorUnPressed = RGB(255,255,255);
    m_crForegroundColor = RGB(0,0,0);

    // Store special button information
    m_lButtonType = lFlags;

    // If the control is already created, return false
    if(m_hWnd != NULL)
    {
        return FALSE;
    }

    // Create the control using CWnd::Create() and bring it to the top
    // Notice the flag WS_CLIPSIBLINGS; this is necessary
    // for proper rendering of composite controls
    if(CWnd::Create(NULL, m_csText, WS_CHILD|WS_VISIBLE|WS_CLIPSIBLINGS, 
                    *lpRect, pParentWnd, nControlID, NULL))
    {
        CWnd::BringWindowToTop();

        return TRUE;
    }
    
    return FALSE;
}

步骤来实现… 在想要使用控件(例如按钮)的窗口/对话框中,定义一个成员变量,该成员变量是指向该控件的指针。隐藏,CodeCSkinnedButton * m_pOkButton复印件; 在对话框OnCreate()或OnInitDialog()的创建逻辑中,在创建用于背景绘制的内存DC的一些初始化之后,插入按钮的创建逻辑。隐藏,OnCreate(LPCREATESTRUCT LPCREATESTRUCT) { 如果(CDialog:: OnCreate (lpCreateStruct) = = 1) { 返回1; } CClientDC直流(这个); m_memDC.CreateCompatibleDC(及直流); m_memBmp.CreateCompatibleBitmap(及直流、1024、768); m_memDC.SelectObject(及m_memBmp); / /其他代码 … / /创建按钮 m_pOkButton = new CSkinnedButton; //分配4个图像id m_pOkButton。SetImageResource(ID_NORMAL, ID_HOVER, ID_PRESSED, ID_DISABLED); //这个标志(true)表示按钮 //是一个不规则形状,将使用 // a透明算法,达到预期效果 m_pOkButton.SetShapedFlag(真正的); / /其他代码 … } 按钮创建和渲染的自定义代码在CSkinnedButton类中实现,如下所示: 隐藏,收缩,复制Codeint CSkinnedButton::OnCreate(LPCREATESTRUCT lpCreateStruct) { 如果(CWnd:: OnCreate (lpCreateStruct) = = 1) 返回1; CClientDC直流(这个); CBitmap bmpTemp; m_dcMemory.CreateCompatibleDC(及直流); 如果(bmpTemp.CreateCompatibleBitmap(和直流、m_nWidth m_nHeight) ! = 0) { m_dcMemory.SelectObject(及bmpTemp); 如果(PrepareFont ()) { } UpdateMemoryDC (); / /创建地区如果不规则形状 如果(m_bShape) { m_hRgn =应用(0,0,0,0); 如果(m_hRgn ! = NULL) { 如果(GetWindowRgn (m_hRgn) = =错误) { m_hRgn =零; 返回1; } } 其他的 { 返回1; } } } 返回0; } int CSkinnedButton: UpdateMemoryDC () { 位图bmpTemp; memset(和bmpTemp 0 sizeof(位图)); 如果(m_dcMemory = = NULL) { 返回1; } # ifdef USE_GIF_IMAGES 如果(m_hBitmap ! =零,,m_hBitmap = = GetCurrentStateBitmap ()) { 返回1; } m_hBitmap = GetCurrentStateBitmap (); 其他# hBitmap = GetCurrentStateBitmap (); # endif 如果(m_hBitmap ! = NULL) { :: GetObject (m_hBitmap sizeof(位图),bmpTemp); m_lImageWidth = bmpTemp.bmWidth; m_lImageHeight = bmpTemp.bmHeight; :: SelectObject (m_dcMemory.GetSafeHdc (), m_hBitmap); } else if (m_nPressedID = = 1,,m_nUnPressedID = = 1,,m_nHoverID = = 1) { CClientDC直流(这个); m_dcMemory.SetMapMode (dc.GetMapMode ()); m_dcMemory.SetWindowExt (dc.GetWindowExt ()); m_dcMemory.SetViewportExt (dc.GetViewportExt ()); m_dcMemory。SetWindowOrg (0,0); CBitmap cbmpTemp; cbmpTemp.CreateCompatibleBitmap(及直流m_nWidth m_nHeight); 如果(m_dcMemory.SelectObject(及cbmpTemp) ! = NULL) { m_dcMemory。FillSolidRect (0, 0, m_nWidth m_nHeight, GetCurrentBackgroundColor ()); } } / /这是不规则形状代码最重要的部分 如果(m_bShape ! = 1,,m_bFindEdges) { m_bFindEdges = FALSE; FindControlEdge(这和m_dcMemory、COLOR_MAGENTA m_hRgnWindow); } 返回0; } FindControlEdge()(不是一个非常直观的名字!)实现了透明的算法,使用红色颜色面具,遍历图像,剪一个地区。你可能会认为,为什么不使用GDI函数TransparentBlt()来达到相同的。好点!然而,当我试图使用TransparentBlt实现,它没有运行在Windows 98 SE(尽管女士声称支持版本的Windows !)。不管怎么说,可能是我没有正确的补丁的Windows或SDK。我决定写我自己的。你有一个选择使用TransparentBlt将承诺在我的技术优化性能确定;) 同时,我的技术引入了严格要求的所有图像受四个像素的红色背景! !例子: 那些可能面临类似的问题TransparentBlt()可以自由使用这里显示的算法或一个你自己的。 隐藏,收缩,复制代码/ /通过形象和创建这个函数遍历 / /区域消除“红色”像素并设置窗口句柄 BOOL FindControlEdge (CWnd * pWnd,疾控中心* dcControl, 也就是说colToSkip HRGN和HRGN) { int nCurrentX = 0; int nCurrentY = 0; int nTempX = 0; int nTempY = 0; BOOL bStop = FALSE; int nDirection = 0; int nCurDirection = 0; int nFirstX = 0; int nFirstY = 0; int nXMap = 0; int nYMap = 0; int nIterate = 0; 点ptTempCoord; CList<点,在ptCoord; 绘图用的矩形类rcWindow (0, 0, 0, 0); 绘图用的矩形类rcClient (0, 0, 0, 0); pWnd→GetWindowRect(及rcWindow); pWnd→GetClientRect(及rcClient); pWnd→ClientToScreen(及rcClient); nXMap = rcClient。左- rcWindow.left; nYMap = rcClient。最高- rcWindow.top; nIterate = 0; bStop = FALSE; nCurrentX = 0; nCurrentY = 0; nDirection =东南; nFirstX = 0; nFirstY = 0; 而(! bStop) { 如果((dcControl→获取像素(nCurrentX + 1, nCurrentY + 1)) ! = colToSkip) { bStop = TRUE; 如果(nCurrentX = = 0,,nCurrentY = = 0) { 返回错误; } } 其他的 { nCurrentX + +; nCurrentY + +; } } bStop = FALSE; 而(! bStop) { nIterate + +; 开关(nDirection) { 例:东南部 如果((dcControl→获取像素(nCurrentX + 1, nCurrentY + 1)) ! = colToSkip) { nDirection =东; 继续; } 其他的 { 数控urrentX + +; nCurrentY + +; } 打破; 东: 如果((dcControl→获取像素(nCurrentX + 1, nCurrentY)) ! = colToSkip) { nDirection =东北; 继续; } 其他的 { nCurrentX + +; } 打破; 东北地区: 如果((dcControl→获取像素(nCurrentX + 1, nCurrentY-1)) ! = colToSkip) { nDirection =北; 继续; } 其他的 { nCurrentX + +; nCurrentY——; } 打破; 北: 如果((dcControl→获取像素(nCurrentX nCurrentY-1)) ! = colToSkip) { nDirection =西北; 继续; } 其他的 { nCurrentY——; } 打破; 西北: 如果((dcControl→获取像素(nCurrentX-1 nCurrentY-1)) ! = colToSkip) { nDirection =西方; 继续; } 其他的 { nCurrentX——; nCurrentY——; } 打破; 西方: 如果((dcControl→获取像素(nCurrentX-1 nCurrentY)) ! = colToSkip) { nDirection =西南; 继续; } 其他的 { nCurrentX——; } 打破; 西南地区: 如果((dcControl→获取像素(nCurrentX-1, nCurrentY + 1)) ! = colToSkip) { nDirection =南; 继续; } 其他的 { nCurrentX——; nCurrentY + +; } 打破; 南: 如果((dcControl→获取像素(nCurrentX, nCurrentY + 1)) ! = colToSkip) { nDirection =东南; 继续; } 其他的 { nCurrentY + +; } 打破; } nCurDirection = nDirection; 如果((dcControl→获取像素(nCurrentX + 1, nCurrentY + 1)) ! = colToSkip) { nDirection =东南; } 如果((dcControl→获取像素(nCurrentX + 1, nCurrentY)) ! = colToSkip) { nDirection =东; } 如果((dcControl→获取像素(nCurrentX + 1, nCurrentY-1)) ! = colToSkip) { nDirection =东北; } 如果((dcControl→获取像素(nCurrentX nCurrentY-1)) ! = colToSkip) { nDirection =北; } 如果((dcControl→获取像素(nCurrentX-1 nCurrentY-1)) ! = colToSkip) { nDirection =西北; } 如果((dcControl→获取像素(nCurrentX-1 nCurrentY)) ! = colToSkip) { nDirection =西方; } 如果((dcControl→获取像素(nCurrentX-1, nCurrentY + 1)) ! = colToSkip) { nDirection =西南; } 如果((dcControl→获取像素(nCurrentX, nCurrentY + 1)) ! = colToSkip) { nDirection =南; } 点ptTemp; 如果(ptCoord.GetCount()在0) { ptTemp = ptCoord.GetTail (); } 其他的 { ptTemp。x = 0; ptTemp。y = 0; } 如果(nCurrentX ! = ptTemp。x | | nCurrentY ! = ptTemp.y) { nTempX = nCurrentX; nTempY = nCurrentY; 开关(nCurDirection) { 北: 西北: nTempX + +; 打破; 东北地区: 东: nTempY + +; 打破; } ptTempCoord。x = nTempX; ptTempCoord。y = nTempY; ptCoord.AddTail (ptTempCoord); } 如果(nFirstX = = 0,,nFirstY = = 0) { nFirstX = nCurrentX; nFirstY = nCurrentY; } else if (nCurrentX = = nFirstX,,nCurrentY = = nFirstY) { 打破; } } 点* ptAll; ptAll = new点[ptCoord.GetCount ()); int nLen = ptCoord.GetCount (); for (int idx = 0;idx< nLen;idx + +) { ptAll [idx] = ptCoord.GetHead (); ptCoord.RemoveHead (); } hRgn = CreatePolygonRgn (ptAll nLen,备用); 删除[]ptAll; 如果(hRgn ! = NULL) { 如果(pWnd→SetWindowRgn (hRgn,真)! = 0) { 返回TRUE; } } 返回错误; } 最后,您实现消息处理程序来控制反应事件和消息(LButtonDown /按钮,OnChar编辑,等等),并适当地玩不同控制状态(正常、悬停、残疾人,等等)和更新相应的“看”通过调用UpdateMemoryDC()。 一些好处…… 如果如果设计得当,你可以为你的应用程序提出并行的“主题”;基本上,不同的图像集叠加在您的应用程序和控件上,并使用配置文件轻松切换。 本文转载于:http://www.diyabc.com/frontweb/news12204.html

原文地址:https://www.cnblogs.com/Dincat/p/13473705.html