1. 程式人生 > 實用技巧 >“剝皮”UI控制元件庫(vc++)

“剝皮”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