Microsoft Developer Network > 論壇首頁 > Visual Studio 論壇 > Visual C++ > 如何在CBitmapButton類別的按鈕貼上透明圖?

已答覆 如何在CBitmapButton類別的按鈕貼上透明圖?

  • 2007年4月8日 下午 01:42
     
     
    各位好:
    我必須寫一個程式,在一個Dialog Box中要有許多使用CBitmapButton的按鈕,
    在參照MSDN的範例之後,寫了以下的程式碼:

    在CDialog類別的Header file中宣告以下的成員:

    class CMyDialog : public CDialog
    {
    public:
        .
        .
        .
        CBitmapButton m_btnBitmapButton;
        BOOL m_bButtonChecked;
        void m_fnDrawTransparent(int x, int y, CDC *pDC, CBitmap *pBitmap,
                                 COLORREF Color);
        .
        .
        .
        afx_msg void OnBmpBtn();
        .
        .
        .
    }

    在Resource.h中定義按鈕的識別碼
    (我並沒有用VC的IDE先放上按鈕,因為按鈕的位置與大小都是動態的)
    #define IDC_BTN_BMPBTN  1020

    在資源編輯器中Import兩張BMP格式的圖檔,並給定字串類型的識別碼
    "BUTTONU"
    "BUTTOND"

    在CPP中,在Dialog的constructor給定成員變數初值
    m_bButtonChecked = FALSE;

    在Dialog的OnInitDialog建立按鈕物件
    BOOL CMyDialog :: OnInitDialog()
    {
        .
        .
        .
        m_btnBitmapButton.Create("", WS_CHILD|WS_VISIBLE|BS_OWNERDRAW,
                                 CRect(10, 10, 10, 10), this, IDC_BTN_BMPBTN);

        if( ! m_btnBitmapButton.LoadBitmaps(_T("BUTTONU"), _T("BUTTOND")) )
        {
            TRACE0("Failed to load bitmaps for button\n");
            AfxThrowResourceException();  
        }

        m_btnBitmapButton.SizeToContent();
    }

    在Event Handler的程式碼
    void CMyDialog :: OnBmpBtn()
    {
       if(m_bButtonChecked)
       {
          m_btnBitmapButton.SetState( FALSE );
          m_bButtonChecked = FALSE;
       }
       else
       {
          m_btnBitmapButton.SetState( TRUE );
          m_bButtonChecked = TRUE;
       }
    }

    以上的程式碼,在經過編譯與執行後,會在Dialog Box的左上角位置出現一個方塊,
    方塊內有一張BMP圖(就是我在資源編輯器中Import的圖),這是一個CBitmapButton類別的
    按鈕,型態為平面、沒有Focus的框,按下時也沒有按鈕下陷的效果,當滑鼠在方塊上單點
    一下時,方塊內的BMP圖會替換成另一張,再按一下又會替換回原來那張圖。

    整個程式執行是正常的,唯獨有個問題不符合我的需求,那就是BMP圖會有個外框(以我
    這個例子來說是會有白色的背景),但我需要讓BMP圖顯示時是看不到白色背景框的,也
    就是所謂的透明圖,因為我有多張不規則外型的圖,一張圖會是一個按鈕,而且這幾張
    圖的邊緣能夠在畫面上拼接成一張大圖(就像是拼圖一樣),因此若有白色背景框,那麼
    相鄰的圖將會被白色背景框所覆蓋。

    因此我找了很多網站,也查閱了MSDN,希望能找到既使用CBitmapButton類別,又能使用
    透明圖的sample code,但找到的都是繼承CButton類別後改寫的類別,而且功能都很複雜
    ,而我實在沒有時間去把整個程式搞清楚之後再移植有用的部分到我的程式中。

    另外,由於我的圖檔來源只有BMP格式,因此使用GIF格式的透明圖也是我無法採用的解決
    方法,因此我也找到將BMP格式載入後轉換為透明圖的方法,這部份測試也是沒問題的。

    因此我的問題便是,我該如何在建立一個CBitmapButton類別的按鈕後,將處理過的透明
    圖動態的貼到Button上?如果我必須繼承一個CBitmapButton的類別並改寫DrawItem成員
    函式,那麼方法與步驟分別是如何?

    我有嚐試先以上面程式碼的方法建立按鈕後,取得該按鈕物件的DC,然後針對這個DC做
    透明圖的處理,但卻失敗了,程式碼如下:

    CDC *pDCTest = NULL;
    CBitmap bmpTest;

    pDCTest = m_btnBitmapButton.GetDC();
    bmpTest.FromHandle( m_btnBitmapButton.GetBitmap() );
    Color = RGB(0xFF, 0xFF, 0xFF);

    m_fnDrawTransparent(0, 0, pDCTest, &bmpTest, Color);

    當執行到m_fnDrawTransparent這個函式時,會在函式內的第二行出現錯誤:
    void CMyDialog :: m_fnDrawTransparent(int x, int y, CDC *pDC, CBitmap *pBitmap,
                                        COLORREF Color)
    {
       BITMAP bm;
       pBitmap->GetObject( sizeof( BITMAP ), &bm );    <-在這行會執行錯誤
       .
       .
       .
    }

    我查過之後,發現是因為pBitmap的m_hobject為NULL造成的,我想是因為我從按鈕取得
    BMP圖到CBitmap物件的方式是錯的,但我不知該如何解決這個問題。

    我另外也試了另一個方法,就是不由按鈕中取出BMP圖,而是另外載入同樣的圖,我
    的做法如下:

    CDC *pDCTest = NULL;
    CBitmap bmpTest;

    pDCTest = m_btnBitmapButton.GetDC();
    bmpTest.LoadBitmap( "BUTTONU" );
    Color = RGB( 0xFF, 0xFF, 0xFF );

    m_fnDrawTransparent(0, 0, pDCTest, &bmpTest, Color);

    以上的方法是可以執行的,但透明圖卻被原本有白色背景框的圖覆蓋了,同樣的我還是
    沒找到解決的辦法。

    因此希望若有哪位高手知道該怎麼做的,或者知道我錯在哪裡的,能夠告訴我如何修改,
    感激不盡。

解答

  • 2007年4月9日 下午 12:05
     
     已答覆
     Richard Huang 寫信:
      

    我有試過先建立一個CBitmapButton的按鈕,然後取得該按鈕的DC,再針對那個DC物件做您所提到的透空處理,測試的結果是雖然可以透空,但是一定會被建立CBitmapButton時所使用的那張圖片給覆蓋住,我不知道是否是我在處理時漏了什麼步驟?

     

    不是很理解您這邊所說的意思,

     

    但您如果直接用 Button 物件的 LoadBitmap 將圖 Load 進來,那進行透空處理就沒意義了,

    因為透空處理並不會改變原本的那張圖,他只是利用位元運算達到透空的效果

     

    我的作法是

     

    1. 建立 CBitmap 物件,用 CBitmap 的 LoadBitmap 或是用 ::LoadImage 將圖載入

    2. 對圖進行透空處理,將所需要的通通畫在一個 memory DC 上

    3. 建立 Button

    4. 利用 Button 的 DrawItem 將 memory DC 裡的圖畫上去

     

    當然其中還有一些細節,像是利用 BITMAP 取得載入圖檔的大小以便對按鈕進行調整等等,

    不過主要的做法大概就是這樣

     

    直接繼承 CButton 而不使用 CBitmapButton 是因為用 CButton 就夠了

     

    只能利用 Bitmap 本身就已經有許多限制,要達到您所想要的效果只能拐個彎做,

    否則用 DirectX 就快多了,透空色可以直接指定,甚至可以直接載入 Targa 檔,

    一行就搞定.....

  • 2008年1月30日 上午 08:37
     
     已答覆

     veirty 寫信:
    請問一下
    利用 BITMAP 取得載入圖檔的大小以便對按鈕進行調整
    該怎麼寫押??

     

    可依如下方式處理

     

    1. 用 CBitmap 物件將 Image 載入

    2. 用 CBitmap.GetObject 丟給 BITMAP 結構

    3. 使用 BITMAP.bmWidth 和 BITMAP.bmHight 取得該圖的長和寬

    4. 依據所得的長寬建立矩形 CRect

    5. 用所建立的矩形作為參數傳入 CButton.Create 中

     

    Thanks,

    Wade

     

所有回覆

  • 2007年4月9日 上午 09:41
     
     

     Richard Huang 寫信:



    另外,由於我的圖檔來源只有BMP格式,因此使用GIF格式的透明圖也是我無法採用的解決
    方法,因此我也找到將BMP格式載入後轉換為透明圖的方法,這部份測試也是沒問題的。

     

    想請問一下您這邊將 BMP 轉為透空圖是怎麼做的?

     

    這邊想提供您一個想法讓您試試看,

    一般我做透空是先建立一個 memory DC,利用位元運算的方式建立貼圖的遮罩,

    然後將遮罩和圖都貼在 memory DC 上 ,最後就畫那個 memory DC 即可。

     

    位元運算建立遮罩是利用 BitBlt 中的最後一個參數 (raster-operation code),

    只要運用 NOTSRCCOPY, SRCAND, SRCPAINT (就是 not, and, or三種運算)就可以透空您想透空的顏色,

    當然前提是您要透空的背景色是單一顏色,不然透空會透的不乾淨(也就是說作圖時千萬不要開柔邊效果),

    我想這部分市面上的 MFC 參考書都會提到 (在 Bitmap 章節),

    您可以參考看看。

     

    我沒試過用 CBitmapButton 能不能這樣貼上去,

    通常我是自訂一個繼承自 CButton 的類別,

    然後用 DrawItem 畫按鈕,使用起來滿方便的

     

     

  • 2007年4月9日 上午 11:27
     
     

    我使用的透空處理的方法就是您所提到的方法。

     

    我有試過先建立一個CBitmapButton的按鈕,然後取得該按鈕的DC,再針對那個DC物件做您所提到的透空處理,測試的結果是雖然可以透空,但是一定會被建立CBitmapButton時所使用的那張圖片給覆蓋住,我不知道是否是我在處理時漏了什麼步驟?

     

    繼承CButton類別,然後用DrawItem畫圖也是我在網路上找了很多他人改寫的元件之後想用的方法,但是,由於我對於MFC尚不是很熟悉,所以不知道步驟該如何?也才會貼文請教。

     

    DrawItem是CButton的Overridable member,而CBitmapButton又是繼承CButton的類別,那麼是否我可以繼承CBitmapButton,然後override DrawItem這個member呢?還是我一定得繼承CButton?因為我看到網路上有關Bitmap Button的元件,幾乎都是繼承CButton的。

  • 2007年4月9日 下午 12:05
     
     已答覆
     Richard Huang 寫信:
      

    我有試過先建立一個CBitmapButton的按鈕,然後取得該按鈕的DC,再針對那個DC物件做您所提到的透空處理,測試的結果是雖然可以透空,但是一定會被建立CBitmapButton時所使用的那張圖片給覆蓋住,我不知道是否是我在處理時漏了什麼步驟?

     

    不是很理解您這邊所說的意思,

     

    但您如果直接用 Button 物件的 LoadBitmap 將圖 Load 進來,那進行透空處理就沒意義了,

    因為透空處理並不會改變原本的那張圖,他只是利用位元運算達到透空的效果

     

    我的作法是

     

    1. 建立 CBitmap 物件,用 CBitmap 的 LoadBitmap 或是用 ::LoadImage 將圖載入

    2. 對圖進行透空處理,將所需要的通通畫在一個 memory DC 上

    3. 建立 Button

    4. 利用 Button 的 DrawItem 將 memory DC 裡的圖畫上去

     

    當然其中還有一些細節,像是利用 BITMAP 取得載入圖檔的大小以便對按鈕進行調整等等,

    不過主要的做法大概就是這樣

     

    直接繼承 CButton 而不使用 CBitmapButton 是因為用 CButton 就夠了

     

    只能利用 Bitmap 本身就已經有許多限制,要達到您所想要的效果只能拐個彎做,

    否則用 DirectX 就快多了,透空色可以直接指定,甚至可以直接載入 Targa 檔,

    一行就搞定.....

  • 2007年4月11日 上午 03:06
     
     
    感謝您的指導,我已經把問題解決了。
  • 2007年11月7日 上午 08:40
     
     
    請問一下
    利用 BITMAP 取得載入圖檔的大小以便對按鈕進行調整
    該怎麼寫押??
  • 2008年1月30日 上午 08:37
     
     已答覆

     veirty 寫信:
    請問一下
    利用 BITMAP 取得載入圖檔的大小以便對按鈕進行調整
    該怎麼寫押??

     

    可依如下方式處理

     

    1. 用 CBitmap 物件將 Image 載入

    2. 用 CBitmap.GetObject 丟給 BITMAP 結構

    3. 使用 BITMAP.bmWidth 和 BITMAP.bmHight 取得該圖的長和寬

    4. 依據所得的長寬建立矩形 CRect

    5. 用所建立的矩形作為參數傳入 CButton.Create 中

     

    Thanks,

    Wade