天天看点

用VC6扩展CButton类制作风格独特的按钮

一、 本文介绍一个CButton的派生类CLinkButton,用此派生类制作的按钮具有以下特点:

1、按钮的外观类似静态控件类CStatic 产生的对象。(参见图一)

用VC6扩展CButton类制作风格独特的按钮

图一

2、当鼠标的光标移到按钮上,但并未按下时,光标改变形状,字体改变形状;按钮类似应用在工具条和菜单上的扁平钮效果。(参见图二)

用VC6扩展CButton类制作风格独特的按钮

图二

3、当按钮按下的情形:(参见图三)

用VC6扩展CButton类制作风格独特的按钮

图三

二、下面具体描述这种按钮的实现方法和步骤:

在VC6的IDE环境中,生成一个基于对话框的PROJECT。 将对话框资源中按钮的属性页打开,在“Style”标签页中选取按钮的“Owner Draw”(自绘)属性。 将光标引入到应用程序的资源中。 利用CLASSWIZARD,用CButton为基类,派生一个新类:CLinkButton。 在派生类中重载基类CButton的虚函数:

<a href="http://www.vckbase.com/index.php/wv/8#viewSource">view source</a>

<code>1.</code><code>virtual</code> <code>void</code> <code>DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)</code>

之所以要重载这个函数是因为选择了按钮的 “Owner Draw”属性后,当按钮的可视行为发生变化时,应用程序的框架要调用这个函数来重新绘制按钮。

定制以下的消息处理:

<code>1.</code><code>afx_msg</code><code>void</code> <code>OnMouseMove(</code><code>UINT</code> <code>nFlags, CPoint point);</code>

<code>2.</code><code>afx_msg</code><code>BOOL</code> <code>OnSetCursor(CWnd* pWnd,</code><code>UINT</code> <code>nHitTest,</code><code>UINT</code> <code>message);</code>

<code>3.</code><code>afx_msg</code><code>void</code> <code>OnTimer(</code><code>UINT</code> <code>nIDEvent);</code>

<code>4.</code><code>afx_msg</code><code>void</code> <code>OnLButtonUp(</code><code>UINT</code> <code>nFlags, CPoint point);</code>

<code>5.</code><code>afx_msg</code><code>void</code> <code>OnLButtonDown(</code><code>UINT</code> <code>nFlags, CPoint point);</code>

<code>6.</code><code>afx_msg</code><code>int</code> <code>OnCreate(LPCREATESTRUCT lpCreateStruct);</code>

<code>7.</code><code>afx_msg</code><code>BOOL</code> <code>OnEraseBkgnd(CDC* pDC);</code>

声明类成员变量定义:

<code>1.</code><code>//定义字体变量</code>

<code>2.</code><code>CFont fUnderline;</code>

<code>3.</code><code>//定义光标变量</code>

<code>4.</code><code>HCURSOR</code> <code>hHand;</code>

<code>5.</code><code>//决定按钮是否按下</code>

<code>6.</code><code>bool</code> <code>bLBtnDown;</code>

<code>7.</code><code>//决定鼠标是否在按钮上</code>

<code>8.</code><code>bool</code> <code>bHighlight;</code>

三、 派生类CLinkButton 的具体实现:

1、重载函数  DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)。

<code>01.</code><code>void</code> <code>CLinkButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) </code>

<code>02.</code><code>{</code>

<code>03.</code><code>    </code><code>// 获取一个CDC指针</code>

<code>04.</code><code>    </code><code>CDC* pDC = CDC::FromHandle(lpDrawItemStruct-&gt;hDC);</code>

<code>05.</code><code>    </code><code>//定义按钮区域并初始化</code>

<code>06.</code><code>    </code><code>CRect rect(lpDrawItemStruct-&gt;rcItem);</code>

<code>07.</code><code>    </code><code>//设置背景模式</code>

<code>08.</code><code>    </code><code>COLORREF</code> <code>oc = pDC-&gt;GetTextColor();</code>

<code>09.</code><code>    </code><code>int</code> <code>iObk = pDC-&gt;SetBkMode(TRANSPARENT);</code>

<code>10.</code><code>    </code><code>//初始化按钮状态</code>

<code>11.</code><code>    </code><code>UINT</code> <code>state = lpDrawItemStruct-&gt;itemState;</code>

<code>12.</code><code>    </code><code>CFont * pOldFont = NULL;</code>

<code>13.</code><code>    </code><code>int</code> <code>iYOffset = 0, iXOffset = 0;</code>

<code>14.</code><code>    </code><code>CString strText;</code>

<code>15.</code><code>    </code><code>GetWindowText(strText);</code>

<code>16.</code><code>    </code><code>rect.top  += iYOffset;</code>

<code>17.</code><code>    </code><code>rect.left += iXOffset;</code>

<code>18.</code><code> </code> 

<code>19.</code><code>    </code><code>if</code> <code>(state &amp; ODS_DISABLED)</code>

<code>20.</code><code>    </code><code>{       </code>

<code>21.</code><code>        </code><code>//按钮置灰(DISABLED)</code>

<code>22.</code><code>        </code><code>CBrush grayBrush;</code>

<code>23.</code><code>        </code><code>grayBrush.CreateSolidBrush (GetSysColor (COLOR_GRAYTEXT));</code>

<code>24.</code><code>        </code><code>CSize sz = pDC-&gt;GetTextExtent(strText);</code>

<code>25.</code><code>        </code><code>int</code> <code>x = rect.left + (rect.Width() - sz.cx)/2;</code>

<code>26.</code><code>        </code><code>int</code> <code>y = rect.top + (rect.Height() - sz.cy)/2;</code>

<code>27.</code><code>        </code><code>rect.top  += 2;</code>

<code>28.</code><code>        </code><code>rect.left += 2;</code>

<code>29.</code><code>        </code><code>pDC-&gt;SetTextColor(GetSysColor(COLOR_3DHIGHLIGHT));</code>

<code>30.</code><code>        </code><code>pDC-&gt;DrawText(strText, rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);</code>

<code>31.</code><code>        </code><code>rect.top  -= 2;</code>

<code>32.</code><code>        </code><code>rect.left -= 2;</code>

<code>33.</code><code>        </code><code>pDC-&gt;SetTextColor(GetSysColor(COLOR_GRAYTEXT));</code>

<code>34.</code><code>        </code><code>pDC-&gt;DrawText(strText, rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);</code>

<code>35.</code><code>    </code><code>}</code>

<code>36.</code><code>    </code><code>else</code>

<code>37.</code><code>    </code><code>{</code>

<code>38.</code><code>        </code><code>if</code> <code>(bHighlight)</code><code>//光标在按钮上</code>

<code>39.</code><code>        </code><code>{</code>

<code>40.</code><code>            </code><code>if</code> <code>(state &amp; ODS_SELECTED)</code>

<code>41.</code><code>                </code><code>//按下按钮</code>

<code>42.</code><code>                </code><code>pDC-&gt;Draw3dRect(rect,GetSysColor(COLOR_3DSHADOW), GetSysColor(COLOR_3DHILIGHT));</code>

<code>43.</code><code>            </code><code>else</code>

<code>44.</code><code>                </code><code>//未按下按钮</code>

<code>45.</code><code>                </code><code>pDC-&gt;Draw3dRect(rect,GetSysColor(COLOR_3DHILIGHT),GetSysColor(COLOR_3DSHADOW));</code>

<code>46.</code><code>                 </code> 

<code>47.</code><code>            </code><code>//字体颜色</code>

<code>48.</code><code>            </code><code>pDC-&gt;SetTextColor(RGB(0,0,255));</code>

<code>49.</code><code> </code> 

<code>50.</code><code>            </code><code>//加下画线(也可以用其他字体)</code>

<code>51.</code><code>            </code><code>if</code> <code>(fUnderline.GetSafeHandle() == NULL)</code>

<code>52.</code><code>            </code><code>{</code>

<code>53.</code><code>                </code><code>CFont * pFont = GetFont();</code>

<code>54.</code><code>                </code><code>ASSERT(pFont);</code>

<code>55.</code><code>                </code><code>LOGFONT lf;</code>

<code>56.</code><code>                </code><code>pFont-&gt;GetLogFont(&amp;lf);</code>

<code>57.</code><code>                </code><code>lf.lfUnderline = TRUE;</code>

<code>58.</code><code>                </code><code>fUnderline.CreateFontIndirect(&amp;lf);     </code>

<code>59.</code><code>            </code><code>}</code>

<code>60.</code><code>            </code><code>pOldFont = pDC-&gt;SelectObject(&amp;fUnderline);</code>

<code>61.</code><code>        </code><code>}</code>

<code>62.</code><code>        </code><code>else</code> <code>pDC-&gt;SetTextColor(GetSysColor(COLOR_BTNTEXT));</code>

<code>63.</code><code> </code> 

<code>64.</code><code>        </code><code>pDC-&gt;DrawText(strText, rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);</code>

<code>65.</code><code>         </code> 

<code>66.</code><code>        </code><code>if</code> <code>(pOldFont) pDC-&gt;SelectObject(pOldFont);</code>

<code>67.</code><code>    </code><code>}</code>

<code>68.</code><code>}</code>

2、定制的消息处理函数

<code>1.</code><code>void</code> <code>CLinkButton::OnMouseMove(</code><code>UINT</code> <code>nFlags, CPoint point) </code>

<code>2.</code><code>{</code>

<code>3.</code><code>    </code><code>//设置一个定时器</code>

<code>4.</code><code>    </code><code>SetTimer(1,10,NULL);</code>

<code>5.</code><code>     </code> 

<code>6.</code><code>    </code><code>CButton::OnMouseMove(nFlags, point);</code>

<code>7.</code><code>}</code>

当鼠标光标移到按钮上时,执行此函数,定时器将发送一个 WM_TIMER消息到消息队列。由OnTimer(UINT nIDEvent)函数处理这个消息。

<code>01.</code><code>void</code> <code>CLinkButton::OnTimer(</code><code>UINT</code> <code>nIDEvent) </code>

<code>03.</code><code>    </code><code>static</code> <code>bool</code> <code>pPainted =</code><code>false</code><code>;</code>

<code>04.</code><code>    </code><code>POINT pt;</code>

<code>05.</code><code>    </code><code>GetCursorPos(&amp;pt);</code>

<code>06.</code><code>    </code><code>CRect rect;</code>

<code>07.</code><code>    </code><code>GetWindowRect (rect);</code>

<code>08.</code><code>    </code><code>if</code> <code>(bLBtnDown)  </code>

<code>09.</code><code>    </code><code>{       </code>

<code>10.</code><code>        </code><code>KillTimer (1);</code>

<code>11.</code><code>        </code><code>if</code> <code>(pPainted) InvalidateRect (NULL);        </code>

<code>12.</code><code>        </code><code>pPainted = FALSE;       </code>

<code>13.</code><code>        </code><code>return</code><code>; </code>

<code>14.</code><code>    </code><code>}</code>

<code>15.</code><code> </code> 

<code>16.</code><code>    </code><code>if</code> <code>(!rect.PtInRect (pt))    </code>

<code>17.</code><code>    </code><code>{       </code>

<code>18.</code><code>        </code><code>bHighlight =</code><code>false</code><code>;</code>

<code>19.</code><code>        </code><code>KillTimer (1);</code>

<code>20.</code><code> </code> 

<code>21.</code><code>        </code><code>if</code> <code>(pPainted)           </code>

<code>22.</code><code>            </code><code>InvalidateRect(NULL);</code>

<code>23.</code><code> </code> 

<code>24.</code><code>        </code><code>pPainted =</code><code>false</code><code>;</code>

<code>25.</code><code>        </code><code>return</code><code>; </code>

<code>26.</code><code>    </code><code>}</code>

<code>27.</code><code>    </code><code>else</code>

<code>28.</code><code>    </code><code>{</code>

<code>29.</code><code>        </code><code>bHighlight =</code><code>true</code><code>;</code>

<code>30.</code><code>        </code><code>if</code> <code>(!pPainted)</code>

<code>31.</code><code>        </code><code>{</code>

<code>32.</code><code>            </code><code>pPainted =</code><code>true</code><code>;</code>

<code>33.</code><code>            </code><code>InvalidateRect(NULL);</code>

<code>34.</code><code>        </code><code>}</code>

<code>36.</code><code> </code> 

<code>37.</code><code>    </code><code>CButton::OnTimer(nIDEvent);</code>

<code>38.</code><code>}</code>

<code>39.</code><code> </code> 

<code>40.</code><code> </code> 

<code>41.</code><code>BOOL</code> <code>CLinkButton::OnSetCursor(CWnd* pWnd,</code><code>UINT</code> <code>nHitTest,</code><code>UINT</code> <code>message) </code>

<code>42.</code><code>{</code>

<code>43.</code><code>    </code><code>if</code> <code>(bHighlight) </code>

<code>44.</code><code>    </code><code>{</code>

<code>45.</code><code>        </code><code>::SetCursor(hHand);</code>

<code>46.</code><code>        </code><code>return</code> <code>true</code><code>;</code>

<code>47.</code><code>    </code><code>}</code>

<code>48.</code><code>     </code> 

<code>49.</code><code>    </code><code>return</code> <code>CButton::OnSetCursor(pWnd, nHitTest, message);</code>

<code>50.</code><code>}</code>

<code>51.</code><code> </code> 

<code>52.</code><code> </code> 

<code>53.</code><code>int</code> <code>CLinkButton::OnCreate(LPCREATESTRUCT lpCreateStruct) </code>

<code>54.</code><code>{</code>

<code>55.</code><code>    </code><code>if</code> <code>(CButton::OnCreate(lpCreateStruct) == -1)</code>

<code>56.</code><code>        </code><code>return</code> <code>-1;</code>

<code>57.</code><code>     </code> 

<code>58.</code><code>    </code><code>CFont * pFont = GetFont();</code>

<code>59.</code><code>    </code><code>ASSERT(pFont);</code>

<code>60.</code><code> </code> 

<code>61.</code><code>    </code><code>LOGFONT lf;</code>

<code>62.</code><code>    </code><code>pFont-&gt;GetLogFont(&amp;lf);</code>

<code>63.</code><code>    </code><code>lf.lfUnderline = TRUE;</code>

<code>64.</code><code> </code> 

<code>65.</code><code>    </code><code>fUnderline.CreateFontIndirect(&amp;lf);</code>

<code>66.</code><code>     </code> 

<code>67.</code><code>    </code><code>return</code> <code>0;</code>

这个函数由框架在显示出按钮之前自动调用,我在这里初始化按钮上显示的字体。

<code>01.</code><code>void</code> <code>CLinkButton::OnLButtonUp(</code><code>UINT</code> <code>nFlags, CPoint point) </code>

<code>03.</code><code>    </code><code>bLBtnDown =</code><code>false</code><code>;</code>

<code>04.</code><code>    </code><code>if</code> <code>(bHighlight) </code>

<code>05.</code><code>    </code><code>{</code>

<code>06.</code><code>        </code><code>bHighlight =</code><code>false</code><code>;</code>

<code>07.</code><code>        </code><code>InvalidateRect(NULL);</code>

<code>08.</code><code>    </code><code>}</code>

<code>09.</code><code>     </code> 

<code>10.</code><code>    </code><code>CButton::OnLButtonUp(nFlags, point);</code>

<code>11.</code><code>}</code>

当按下按钮又放开时调用这个函数。

<code>1.</code><code>void</code> <code>CLinkButton::OnLButtonDown(</code><code>UINT</code> <code>nFlags, CPoint point) </code>

<code>3.</code><code>    </code><code>bLBtnDown =</code><code>true</code><code>;</code>

<code>4.</code><code>     </code> 

<code>5.</code><code>    </code><code>CButton::OnLButtonDown(nFlags, point);</code>

<code>6.</code><code>}</code>

当按下按钮时调用这个函数。

<code>01.</code><code>BOOL</code> <code>CLinkButton::OnEraseBkgnd(CDC* pDC) </code>

<code>03.</code><code>    </code><code>COLORREF</code> <code>cr = GetSysColor(COLOR_3DFACE);</code>

<code>04.</code><code>    </code><code>int</code> <code>r = GetRValue(cr);</code>

<code>05.</code><code>    </code><code>int</code> <code>g = GetGValue(cr);</code>

<code>06.</code><code>    </code><code>int</code> <code>b = GetBValue(cr);</code>

<code>07.</code><code>    </code><code>if</code> <code>(r &gt; 1) r -= 2;</code>

<code>08.</code><code>    </code><code>if</code> <code>(g &gt; 1) g -= 2;</code>

<code>09.</code><code>    </code><code>if</code> <code>(r &lt; 3 &amp;&amp; g &lt; 3 &amp;&amp; b &lt; 253) b += 2;</code>

<code>10.</code><code>    </code><code>COLORREF</code> <code>cr1 = RGB(r,g,b);</code>

<code>11.</code><code>    </code><code>CRect rc;</code>

<code>12.</code><code>    </code><code>GetClientRect(rc);</code>

<code>13.</code><code>    </code><code>pDC-&gt;FillSolidRect(rc, cr1);</code>

<code>14.</code><code>     </code> 

<code>15.</code><code>    </code><code>return</code> <code>CButton::OnEraseBkgnd(pDC);</code>

<code>16.</code><code>}</code>

当按钮的背景需要重画时,应用程序框架调用此函数。

其他实现细节请下载源代码。运行程序的效果图见图一、图二和图三。

继续阅读