MFC 在一個button上畫圖畫的一些方法

最近在做MFC方面的一些應用工作,成這個機會把MFC好好學學,學學沒什麼壞事,儘管MFC已經不是很方便了。


I think some of you may not like the buttons in Windows. Sometimes, I think they're ugly. Fortunately, we can change the appearance of our buttons by overriding the DrawItem function of the CButton class. I 'm going to demonstrate the steps of the owner drawing button. In this article, I will make a class, which inherits from CButton class.

Let 's take a dialog-based MFC application as an example. In the File Menu, click New to add a new project. Then, choose MFC Application Wizard (exe). In the project name, type in OwnerDrawButton (just an example), and then click OK. In step one of the MFC App Wizard, choose Dialog Based. After pressing Finish, you are brought to the "New Project Information" page. You'll ignore this page, so press OK.

The edit window is displaying the IDD_OWNERDRAWBUTTON_DIALOG. We have to make our class first, so we don't have time to look at this dialog. Go to the ClassView, right-click "OwnerDrawButton classes", and choose "New class". For the class type, just leave the default "MFC Class". In the "Name" editbox, type "CMyButton" (just an example). Choose "CButton" from the "Base Class".

You have added a new class; it's time to override the DrawItem function. Right-click "CMyButton" in ClassView and choose "Add Virtual Function". The "New Virtual Override for class CMyButton" page opens. Double-click "DrawItem" in the left "New Virtual Functions" listbox. Afterward, 'DrawItem" will jump to the right "Existing virtual function overrides" listbox. Finally, press OK to add a new virtual override.

Go to the implementation of CMyButton::DrawItem, which is in MyButton.cpp. Add draw code in this function. Here is my drawing code to demonstrate how to use CDC to draw it.

  1. void CMyButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
  2. {
  3. CDC dc;
  4. dc.Attach(lpDrawItemStruct->hDC); //Get device context object
  5. CRect rt;
  6. rt = lpDrawItemStruct->rcItem; //Get button rect
  7.  
  8. dc.FillSolidRect(rt, RGB(0, 0, 255)); //Fill button with blue color
  9.  
  10. UINT state = lpDrawItemStruct->itemState; //Get state of the button
  11. if ( (state & ODS_SELECTED) ) // If it is pressed
  12. {
  13. dc.DrawEdge(rt,EDGE_SUNKEN,BF_RECT); // Draw a sunken face
  14. }
  15. else
  16. {
  17. dc.DrawEdge(rt,EDGE_RAISED,BF_RECT); // Draw a raised face
  18. }
  19.  
  20. dc.SetTextColor(RGB(255,255,120));
  21. // Set the color of the caption to be yellow
  22. CString strTemp;
  23. GetWindowText(strTemp);
  24. // Get the caption which have been set
  25. dc.DrawText(strTemp,rt,DT_CENTER|DT_VCENTER|DT_SINGLELINE);
  26. // Draw out the caption
  27. if ( (state & ODS_FOCUS ) ) // If the button is focused
  28. {
  29. // Draw a focus rect which indicates the user
  30. // that the button is focused
  31. int iChange = 3;
  32. rt.top += iChange;
  33. rt.left += iChange;
  34. rt.right -= iChange;
  35. rt.bottom -= iChange;
  36. dc.DrawFocusRect(rt);
  37. }
  38. dc.Detach();
  39. }

Your job isn't finished yet. Go back to the ResourceView and click "IDD_OWNERDRAWBUTTON_DIALOG". Yes, you are right! You are going to edit your dialog box. Drag a button to the dialog box. Now, modify its properties by right-clicking it and choosing "Properities". For its ID, call it "IDC_COLOREDBUTTON" and caption it "Colored Button". You also have to allow it to be owner draw. Go to the Styles tab and check "Owner Draw". Then, close the Properties dialog box.

You have to link this button to the CMyButton class. Press Ctrl+W to open the MFC Class Wizard. Under the "Member variables" page, double-click "IDC_COLOREDBUTTON", which is in the "Control IDs" listbox. You are then brought to the "Add Member Variable" dialog. For the "Member variable name", type "m_MyColoredButton". For the "Variable Type", choose "CMyButton", and then press OK. VC++ will inform you to check if there is an include statement in "OwnerDrawButtonDlg.h". You won't find it, so add #include "MyButton.h" at the beginning of OwnerDrawButtonDlg.h.

Press F7 to build your project. Afterward, run it and you will see the same as the picture at the beginning of this article.

This is the first time for me to write an English article. Feel free to give some bad comments to me. I am glad to read them.


class CMyClass : public CButton
{
// Construction
public:
	CMyClass();

// Attributes
public:

// Operations
public:

// Overrides
	// ClassWizard generated virtual function overrides
	//{{AFX_VIRTUAL(CMyClass)
	public:
	virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
	//}}AFX_VIRTUAL

// Implementation
public:
	virtual ~CMyClass();

	// Generated message map functions
protected:
	//{{AFX_MSG(CMyClass)
		// NOTE - the ClassWizard will add and remove member functions here.
	//}}AFX_MSG

	DECLARE_MESSAGE_MAP()
};

CMyClass::CMyClass()
{
}

CMyClass::~CMyClass()
{
}


BEGIN_MESSAGE_MAP(CMyClass, CButton)
	//{{AFX_MSG_MAP(CMyClass)
		// NOTE - the ClassWizard will add and remove mapping macros here.
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CMyClass message handlers

void CMyClass::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) 
{
	// TODO: Add your code to draw the specified item
	CDC dc;
	dc.Attach(lpDrawItemStruct->hDC);		//Get device context object
	CRect rt;
	rt = lpDrawItemStruct->rcItem;		//Get button rect

	dc.FillSolidRect(rt, RGB(0, 0, 255));		//Fill button with blue color

	UINT state = lpDrawItemStruct->itemState;	//Get state of the button
	if ( (state & ODS_SELECTED) )		// If it is pressed
	{
		dc.DrawEdge(rt,EDGE_SUNKEN,BF_RECT);    // Draw a sunken face
	}
	else
	{
		dc.DrawEdge(rt,EDGE_RAISED,BF_RECT);	// Draw a raised face
	}
	dc.SetTextColor(RGB(255,255,120));		// Set the color of the caption to be yellow
	CString strTemp;
	GetWindowText(strTemp);		// Get the caption which have been set
	dc.DrawText(strTemp,rt,DT_CENTER|DT_VCENTER|DT_SINGLELINE);		// Draw out the caption
	if ( (state & ODS_FOCUS ) )       // If the button is focused
	{
		// Draw a focus rect which indicates the user 
		// that the button is focused
		int iChange = 3;
		rt.top += iChange;
		rt.left += iChange;
		rt.right -= iChange;
		rt.bottom -= iChange;
		dc.DrawFocusRect(rt);
	}
	dc.Detach();
}

class CAboutDlg : public CDialog
{
public:
	CAboutDlg();

// Dialog Data
	//{{AFX_DATA(CAboutDlg)
	enum { IDD = IDD_ABOUTBOX };
	//}}AFX_DATA

	// ClassWizard generated virtual function overrides
	//{{AFX_VIRTUAL(CAboutDlg)
	protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
	//}}AFX_VIRTUAL

// Implementation
protected:
	//{{AFX_MSG(CAboutDlg)
	//}}AFX_MSG
	DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
	//{{AFX_DATA_INIT(CAboutDlg)
	//}}AFX_DATA_INIT
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CAboutDlg)
	//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
	//{{AFX_MSG_MAP(CAboutDlg)
		// No message handlers
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// COwnerDrawButtonDlg dialog

COwnerDrawButtonDlg::COwnerDrawButtonDlg(CWnd* pParent /*=NULL*/)
	: CDialog(COwnerDrawButtonDlg::IDD, pParent)
{
	//{{AFX_DATA_INIT(COwnerDrawButtonDlg)
		// NOTE: the ClassWizard will add member initialization here
	//}}AFX_DATA_INIT
	// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void COwnerDrawButtonDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(COwnerDrawButtonDlg)
	DDX_Control(pDX, IDC_COLOREDBUTTON, m_MyColoredButton);
	//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(COwnerDrawButtonDlg, CDialog)
	//{{AFX_MSG_MAP(COwnerDrawButtonDlg)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// COwnerDrawButtonDlg message handlers

BOOL COwnerDrawButtonDlg::OnInitDialog()
{
	CDialog::OnInitDialog();

	// Add "About..." menu item to system menu.

	// IDM_ABOUTBOX must be in the system command range.
	ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
	ASSERT(IDM_ABOUTBOX < 0xF000);

	CMenu* pSysMenu = GetSystemMenu(FALSE);
	if (pSysMenu != NULL)
	{
		CString strAboutMenu;
		strAboutMenu.LoadString(IDS_ABOUTBOX);
		if (!strAboutMenu.IsEmpty())
		{
			pSysMenu->AppendMenu(MF_SEPARATOR);
			pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
		}
	}

	// Set the icon for this dialog.  The framework does this automatically
	//  when the application's main window is not a dialog
	SetIcon(m_hIcon, TRUE);			// Set big icon
	SetIcon(m_hIcon, FALSE);		// Set small icon
	
	// TODO: Add extra initialization here
	
	return TRUE;  // return TRUE  unless you set the focus to a control
}

void COwnerDrawButtonDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
	if ((nID & 0xFFF0) == IDM_ABOUTBOX)
	{
		CAboutDlg dlgAbout;
		dlgAbout.DoModal();
	}
	else
	{
		CDialog::OnSysCommand(nID, lParam);
	}
}

// If you add a minimize button to your dialog, you will need the code below
//  to draw the icon.  For MFC applications using the document/view model,
//  this is automatically done for you by the framework.

void COwnerDrawButtonDlg::OnPaint() 
{
	if (IsIconic())
	{
		CPaintDC dc(this); // device context for painting

		SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);

		// Center icon in client rectangle
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// Draw the icon
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialog::OnPaint();
	}
}

// The system calls this to obtain the cursor to display while the user drags
//  the minimized window.
HCURSOR COwnerDrawButtonDlg::OnQueryDragIcon()
{
	return (HCURSOR) m_hIcon;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章