要用到PNG格式的按钮,起初使用CxImage,发现有些地方实现起来很不方便。最终找到了他。 希望对大家有用。 IntroductionThere are several owner draw buttons on this site, but I couldn’t find one that easily supports PNG files with transparency, so I created this class. Since this class uses GDI+, it actually supports many image formats, but the better quality buttons available are now PNG instead of ICO, so that is the one highlighted here. Update: I have an extended version of this class in my Style Toolkit. If all you want is to use an image on a button, this class is probably simpler to use. BackgroundGDI+ is part of the Microsoft Windows SDK, and it needs to be initialized when the application starts up. If you haven’t used GDI+ before, look at the source code of the demo project and read this article: Features
The image at the top of the page shows the play button in three different states. From left to right, they are: normal, highlighted, and disabled. The other two buttons are just more examples that look cool. It may not be too obvious from the picture that the second button is in the highlighted state. This is by design, I don’t want to significantly change the image. The highlight state just increases the brightness and the contrast a bit. It is obvious enough when you move the mouse over it. This class prioritizes image quality, so it will never stretch or shrink the image which usually degrades the quality, it will just paint the part that fits. If you need to resize your image, use an image editor like Photoshop. The picture below shows the play button in the toggled state, and it should be obvious why you would want such a feature. The exit button is in the highlighted state, and the tool tip is shown in this image. There is no performance penalty for using GDI+ (assuming such a penalty exists) since it is only used during initialization. On creation, the images are converted to bitmaps, and the bitmaps are used when the control needs to be repainted. Using the codeStep 1 – Add these files to your project
Step 2 – Add resources, member variables, and imagesAdd a button to your dialog box with the resource editor, set the resource ID, and erase the text in the Caption box. You could set the style to Owner Draw, but you don’t need to because the code will automatically set this style. Using the Class Wizard, add a variable to the ID you just created. In this example, I set the ID to In the resource editor, import the .png file from the res folder and set the resource type to PNG. Using PNG is just convention, it can be anything as long as the code matches what you name it. Right click on Step 3 – Add the LoadStdImage() functionNow, we just need to load the image to the button on initialization. In the BOOL CTestGdipButtonDlg::OnInitDialog() { CDialog::OnInitDialog(); /// a bunch of stuff here m_cPlay.LoadStdImage(IDR_PLAY, _T("PNG")); return TRUE; } Step 4 – Build and RunYou should be able to run it now and see your new PNG button! If it crashes here, it is probably because you didn’t initialize GDI+. Review the Background section of this article. The demo projectThis is all the code that is needed to create the buttons in the test program: // load the standard image and alternate image m_cPlay.LoadStdImage(IDR_PLAY, _T("PNG")); m_cPlay.LoadAltImage(IDR_PAUSE, _T("PNG")); m_cPlay.EnableToggle(TRUE); // just to show highlight state for article m_cPlayHi.LoadStdImage(IDR_PLAY, _T("PNG")); // set as disabled m_cPlayDis.LoadStdImage(IDR_PLAY, _T("PNG")); m_cPlayDis.EnableButton(FALSE); // show a larger button type m_cGear.LoadStdImage(IDR_GEAR, _T("PNG")); // replace the OK button with something m_cShutDn.LoadStdImage(IDR_EXIT, _T("PNG")); m_cShutDn.SetToolTipText(_T("Close Program")); Both the VC6 and VS2005 versions are included in the demo project. Issues with transparent imagesThe button control has no idea what the background beneath it should be; it gets this information from the bitmap associated with the current DC. In most cases, this works fine, the background is what is on the screen just before the control is painted. However, the application may not be the top most when it is launched. An always on top application like Task Manager may be in the way, so when it gets the background image, it is the wrong data. This is overcome by calling the Set all the button backgrounds in the parent’s BOOL CTestGdipButtonDlg::OnEraseBkgnd(CDC* pDC) { CDialog::OnEraseBkgnd(pDC); CRect rect; GetClientRect(rect); CMemDC pDevC(pDC, rect); // fill in the back ground with something SetButtonBackGrounds(pDevC); return TRUE; } void CTestGdipButtonDlg::SetButtonBackGrounds(CDC *pDC) { m_cPlay.SetBkGnd(pDC); m_cPlayHi.SetBkGnd(pDC); m_cPlayDis.SetBkGnd(pDC); m_cShutDn.SetBkGnd(pDC); } Since the DC that is passed is a memory DC, it doesn’t matter what other applications might be doing. The code above assumes you want to use something other than the crummy default background; otherwise, you would probably just use the default crummy button. VC6 Build issuesVC6 needs a few extra things to compile correctly, add the following to your stdafx.h file. Also add the SDK include and lib paths to your environment. // VC6 #if defined(_MSC_VER) && _MSC_VER == 1200 #ifndef ULONG_PTR #define ULONG_PTR unsigned long* #endif #include <Specstrings.h> #include <gdiplus.h> #pragma comment(lib, "gdiplus.lib") using namespace Gdiplus; // VS2005 #else #include <gdiplus.h> #pragma comment(lib, "gdiplus.lib") using namespace Gdiplus; #endif 发现问题:
拿到该程序后,发现原始代码为了显示效果流畅,所需图片已经全部加载到了资源当中。而我需要的是从本地进行读取并显示。在CGdipButton类中怎加了如下函数:
//==============================================================================
对CGdiPlusBitmap也进行了修改:
class CGdiPlusBitmap
public:
void Empty() { delete m_pBitmap; m_pBitmap = NULL; }
operator Gdiplus::Bitmap*() const { return m_pBitmap; }
对于从文件中读取的图像格式,在显示上有问题。需要对CGdipButton::CtlColor(CDC* pScreenDC, UINT nCtlColor)函数部分位置进行修正。 //============================================================================= CBitmap bmp, *pOldBitmap; CRect rect; // do everything with mem dc Gdiplus::Graphics graphics(pDC->m_hDC); // background CRect rect1; GetWindowRect(rect1); m_dcBk.CreateCompatibleDC(&clDC); // standard image graphics.DrawImage(*m_pStdImage, 0, 0); // standard image pressed
ImageAttributes ia; float width = (float)m_pStdImage->m_pBitmap->GetWidth(); RectF grect; grect.X=0, grect.Y=0; grect.Width = width; grect.Height = height; graphics.DrawImage(*m_pStdImage, grect, 0, 0, width, height, UnitPixel, &ia); m_dcStdP.CreateCompatibleDC(pDC); // standard image hot ColorMatrix HotMat = { 1.05f, 0.00f, 0.00f, 0.00f, 0.00f, ImageAttributes ia; float width = (float)m_pStdImage->m_pBitmap->GetWidth(); RectF grect; grect.X=0, grect.Y=0; grect.Width = width; grect.Height = height; graphics.DrawImage(*m_pStdImage, grect, 0, 0, width, height, UnitPixel, &ia); m_dcStdH.CreateCompatibleDC(pDC); // grayscale image ColorMatrix GrayMat = { 0.30f, 0.30f, 0.30f, 0.00f, 0.00f, ImageAttributes ia; float width = (float)m_pStdImage->m_pBitmap->GetWidth(); RectF grect; grect.X=0, grect.Y=0; grect.Width = width; grect.Height = height; graphics.DrawImage(*m_pStdImage, grect, 0, 0, width, height, UnitPixel, &ia); m_dcGS.CreateCompatibleDC(pDC); // alternate image graphics.DrawImage(*m_pAltImage, 0, 0); // alternate image pressed graphics.DrawImage(*m_pAltImage, 1, 1); // alternate image hot ColorMatrix HotMat = { 1.05f, 0.00f, 0.00f, 0.00f, 0.00f, ImageAttributes ia; float width = (float)m_pStdImage->m_pBitmap->GetWidth(); RectF grect; grect.X=0, grect.Y=0; grect.Width = width; grect.Height = height; graphics.DrawImage(*m_pAltImage, grect, 0, 0, width, height, UnitPixel, &ia); m_dcAltH.CreateCompatibleDC(pDC); if(m_pCurBtn == NULL) m_bHaveBitmaps = TRUE; return NULL; |