自绘控件的子类化方法

 

1.       新建一个基于对话框的MFC程序。

2.       在工程中添加一个新类CMyButton,基类选择CButton。

3.       然后转至新生成的MyButton.h头文件中,将鼠标放在类名CMyButton上点击一下,打开“属性”窗口,在“消息”按钮下为此类添加 WM_LBUTTONDOWN,WM_LBUTTONUP消息,并重写DrawItem虚函数(这个函数是重绘按钮时要调用的,在这里面可以自定义一些绘制按钮的操作,必须设置控件属性为 OWNERDRAW才会调用此函数。)。

4.       在MyButton.h头文件中添加一个BOOL型变量m_BtnDown,来判断按钮是否被按下,并在构造函数中初始化为false.

5.       鼠标左键事件的代码:

void CMyButton::OnLButtonDown(UINT nFlags, CPoint point)

{

         // TODO: 在此添加消息处理程序代码和/或调用默认值

         m_BtnDown=true;

         //引起按钮重绘

         Invalidate();

         CButton::OnLButtonDown(nFlags, point);

}

 

void CMyButton::OnLButtonUp(UINT nFlags, CPoint point)

{

         // TODO: 在此添加消息处理程序代码和/或调用默认值

         m_BtnDown=false;

         Invalidate();

         CButton::OnLButtonUp(nFlags, point);

}

注:Invalidate(BOOL bErase=TRUE)函数的作用是使整个窗口的客户区无效。客户区无效就意味着需要重绘。例如,如果一个被其它窗口遮住的窗口变成了前台窗口,那么原来被遮住的部分就是无效的,需要重绘。这时Windows会在应用程序的消息队列中放置WM_PAINT消息。MFC为窗口类提供了WM_PAINT的消息处理函数OnPaint,OnPaint负责重绘窗口。视图类有一些例外,在视图类的OnPaint函数中调用了OnDraw函数,实际的重绘工作由OnDraw来完成。参数bErase为TRUE时,重绘区域内的背景将被擦除,否则,背景将保持不变。

              和UpdateWindow的区别是UpdateWindow是使窗口立即重绘。Invalidate只是发送一个WM_PAINT消息,WM_PAINT消息的优先级很低,需要等到消息队列中其他消息处理完成后才能被处理,所以窗口不会立即刷新。

              窗口重绘时会重新生成按钮,按钮生成时会查看其风格,如果为OwnerDraw风格,则会调用DrawItem函数。关于OwnerDraw风格的设置,后边会讲。

  1. 6.       DrawItem事件重载:

void CMyButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)

{

// TODO:  添加您的代码以绘制指定项

         //通过DRAWITEMSTRUCT结构获取句柄,并且类型转换,这个FromHandle很常用

    CDC* pDC=CDC::FromHandle(lpDrawItemStruct->hDC); 

     //下面两行来获取这个结构中的一个字段

    UINT state=lpDrawItemStruct->itemState;//按钮的状态

   CRect rect=lpDrawItemStruct->rcItem;//重绘的范围,也就是按钮

         //获取按钮上显示的文字

    CString str; 

   GetWindowText(str); 

   if (state & ODS_SELECTED)//如果按钮被选中

    {        

        //绘制一个控件,并制定控件的类型和状态

        pDC->DrawFrameControl(&rect,DFC_BUTTON,DFCS_BUTTONPUSH | DFCS_PUSHED); 

    } 

   else 

   { 

        pDC->DrawFrameControl(&rect,DFC_BUTTON,DFCS_BUTTONPUSH); 

   } 

    if (m_BtnDown) 

    {       //填充矩形

        pDC->FillSolidRect(rect,RGB(255,255,0)); 

    } 

    else 

    { 

        pDC->FillSolidRect(rect,RGB(0,0,255)); 

    } 

    if (!str.IsEmpty()) 

    {     

            //以下是当按钮按下时要稍微移动按钮上文字的位置

            //使用当前字体计算一行文字的宽度和长度以确定尺寸

        CSize Extent=pDC->GetTextExtent(str); 

        CPoint pt=CPoint(rect.CenterPoint().x-Extent.cx/2,rect.CenterPoint().y-Extent.cy/2); 

           //如果被选中

        if (state & ODS_SELECTED) 

        { 

                   //向一个点中添加x,y坐标

            pt.Offset(1,1); 

        } 

         //设置背景样式,值有两个:

         //OPAQUE   在文字,画刷,铅笔绘画之前,背景由当前背景颜色填充。这时默认的背景模式。

         //TRANSPARENT   在绘制前背景不变

        int nMode=pDC->SetBkMode(TRANSPARENT); 

        if (state & ODS_DISABLED) 

        { 

            pDC->DrawState(pt,Extent,str,DSS_DISABLED,TRUE,0,(HBRUSH)NULL); 

        } 

        else 

        { 

            pDC->TextOut(pt.x,pt.y,str); 

        } 

        pDC->SetBkMode(nMode); 

    } 

}

注:

  1. 1.        DrawItem函数用于重绘单个控件。其参数为指向DRAWITEMSTRUCT的一个长指针。

DRAWITEMSTRUCT 为需要自绘的控件或者菜单项提供了必要的信息。在需要绘制的控件或者菜单项对应的WM_DRAWITEM消息函数中得到一个指向该结构的指针。

  1. 2.        typedef struct tagDRAWITEMSTRUCT {

         UINT CtlType;

         UINT CtlID;

          UINT itemID;

         UINT itemAction;

         UINT itemState;

         HWND hwndItem;

         HDC hDC;

         RECT rcItem;

         DWORD itemData;

} DRAWITEMSTRUCT;

CtlType:指定参数类型。例:ODT_BUTTON、ODT_COMBOBOX、ODT_LISTBOX等。

CtlID:combo box, list box, 或者 button的控件ID。菜单项不需要此参数。

itemID:菜单项的ID,也可以表示列表框或者组合框中某项的索引值。对于一个空的列表框或组合框此值为-1.这时应用程序只绘制焦点矩形(该矩形的坐标有rcItem成员给出)。虽然此时控件没有需要显示的项,但绘制焦点矩形还是很有必要的。因为这样能够提示用户该控件是否具有输入焦点。当然也可设置itemAction的值,使得无需绘制焦点。

itemAction:指定绘制行为,可取多值。ODA_DRAWENTIRE:当整个控件都需要被绘制时,设置该值。ODA_FOCUS:控件在获得或是去焦点时被绘制。此时应该检查itemState成员,以确定控件是否具有输入焦点。ODA_SELECT:控件在选中状态改变时被重绘。此时应检查itemState成员,以确定控件是否被选中。

itemState:指定当前绘制操作完成后,所绘项的可见状态。

ODS_CHECKED:菜单项被选中。该值只对菜单项有用。

              ODS_COMBOBOXEDIT:在自绘组合框控件中只绘制选择区域。

              ODS_DEFAULT:默认值。

              ODS_DISABLED:控件被禁止,则设置该值。

              ODS_FOCUS:控件需要输入焦点,则设置该值。

              ODS_GRAYED:控件被灰色显示。该值只在绘制菜单时使用。

              ODS_HOTLIGHT:Windows XP: 如果鼠标指针位于控件之上,则设置该值,这时控件会显示高亮颜色。

           ODS_INACTIVE: Windows XP: 表示没有激活的菜单项。

              ODS_NOACCEL:Windows XP: 控件是否有快速键盘。

              ODS_NOFOCUSRECT:Windows XP,不绘制捕获焦点的效果。

              ODS_SELECTED:选中的菜单项。

hwndItem:指定了组合框、列表框和按钮等自绘控件的窗口句柄;如果自绘的对象是菜单项,则表示包含该菜单项的菜单句柄。

hDC:指定了绘制操作所使用的设备环境。

rcItem:指定了将被绘制的矩形区域。这个矩形区域就是上面hDC的作用范围。系统会自动裁剪组合框、列表框或按钮等控件的自绘制区域以外的部分。也就是说rcItem中的坐标点(0,0)指的就是控件的左上角。但是系统不裁剪菜单项,所以在绘制菜单项的时候,必须先通过一定的换算得到该菜单项的位置,以保证绘制操作在我们希望的区域中进行。

  1. 3.        DC(设备上下文):设备上下文是一种包含有关某个设备(如显示器或打印机)的绘制属性信息的 Windows 数据结构。所有绘制调用都通过设备上下文对象进行,这些对象封装了用于绘制线条、形状和文本的 Windows API。设备上下文允许在 Windows 中进行与设备无关的绘制。设备上下文可用于绘制到屏幕、打印机或者图元文件

hDC是DC的一个DC的句柄。CDC是所有DC的基类。

  1. 4.        FromHandle(HANDLE h)从网上查找的解释是,先进行查找,如果h与某个CWnd(窗口)对象相关联,就返回此CWnd对象的指针;如果h没有与任何CWnd对象关联,则new一个新的CWnd对象返回。
  2. 7.       自定义类与标准控件的关联

方法一:设置按钮的属性为Owner Drawn.

方法二:在程序中,重载PreSubClassWindow。此函数在CWnd::Create和DDX_Contorl之后调用,即在窗口子类化创建后和窗口显示前调用。在此函数中添加ModifyStyle(0,BS_OWNERDRAW);此函数用于改变控件的类型。

  1. 8.       创建子类化的自绘按钮

   方法一、在创建按钮时可以给这个按钮绑定一个CMyButton类对象,但是这个时候在classwizard中还不能看到这个类:  可以删掉当前目录下的clw文件,然后重新打开程序,按下ctrl+w,然后一般选择add all,点击ok,这个时候再添加类对象,就会发现这个类了

方法二、上述方法不适合动态添加,下面用程序的方法动态添加

在窗口中拖入一个Button控件。在Dlg的头文件中加入成员函数CMyButton m_MyButton(注意要加入头文件)。然后在此窗口的OnInitDialog函数中调用m_MyButton.SubClassDlgItem(IDC_BUTTON1,this).此函数可以动态的子类化一个由此窗口创建的控件并将此控件关联至此CWnd.

如果你已经有了一个窗口的指针,或者你的工作在一个CView或者其他CWnd的派生类中且里面的控件被动态创建,或者你不想用上边的函数,你可以使用下述方法。

CWnd* pWnd = GetDlgItem(IDC_BUTTON1); m_btnMyButton.SubclassWindow(pWnd->GetSafeHwnd());

时间: 2024-09-20 19:40:25

自绘控件的子类化方法的相关文章

android实现图片验证码方法解析(自绘控件)

自绘控件的内容都是自己绘制出来的 大致流程如下: 1.定义一个类继承view 1.使用TypedArray初始化属性集合     在view的构造方法中 有一个AttributeSet的参数 很明显是用来保存控件属性信息的 我们也的确可以通过循环然后用键值对的方式获取信息 而TypedArray是用来简化我们的工作的 2.重写onMeasure 测量控件大小 3.重写onDraw 绘制控件 2.根据需求在attrs文件中自定义属性 declare-styleable 声明自定义属性可以自定义一个

MFC自绘控件学习总结

前言:从这学期开始就一直在学习自绘控件(mfc),目标是做出一款播放器界面,主要是为了打好基础,因为我基础实在是很烂....说说我自己心得体会以及自绘控件的方法吧,算是吐槽吧,说的不对和不全的地方,或者有更好的方法,请不吝赐教. 我的机器环境是:Windows7旗舰版 Service Pack 1,Visual studio 2005 1).重绘某个控件时,强烈推荐使用子类化方法,比如想自绘Button控件, 首先添加自己的类CMYButton 继承自 CButton ,声明一个CMYButto

MFC基础,MFC自绘控件学习总结.---转

前言:从这学期开始就一直在学习自绘控件(mfc),目标是做出一款播放器界面,主要是为了打好基础,因为我基础实在是很烂....说说我自己心得体会以及自绘控件的方法吧,算是吐槽吧,说的不对和不全的地方,或者有更好的方法,请不吝赐教. 我的机器环境是:Windows7旗舰版 Service Pack 1,Visual studio 2005 1).重绘某个控件时,强烈推荐使用子类化方法,比如想自绘Button控件, 首先添加自己的类CMYButton 继承自 CButton ,声明一个CMYButto

MFC自绘控件学习总结第二贴---转

首先感谢大家对第一帖的支持,应一些网友烈要求下面我在关于上一贴的一些补充和说明(老鸟可以无视)这一贴是实战+理论不知道第一帖的先看第一帖:http://topic.csdn.net/u/20110710/19/5209f358-31c8-4057-b108-02155a417fd0.html 1).补充个高级可重载函数PreSubclassWindow(),我的理解是允许用户在子类化之前再做一额外些处理 ,这个重载函数也是非常重要的,要引起相当的注意.可以在这里改变控件的大小,位置,窗口样式,字

MFC自绘控件学习总结第二贴

首先感谢大家对第一帖的支持,应一些网友烈要求下面我在关于上一贴的一些补充和说明(老鸟可以无视)这一贴是实战+理论不知道第一帖的先看第一帖:http://topic.csdn.net/u/20110710/19/5209f358-31c8-4057-b108-02155a417fd0.html 1).补充个高级可重载函数PreSubclassWindow(),我的理解是允许用户在子类化之前再做一额外些处理 ,这个重载函数也是非常重要的,要引起相当的注意.可以在这里改变控件的大小,位置,窗口样式,字

Android 自绘控件

由于公司项目的需求,需要绘制一条竖直的间断线作为分割线.这个可坑了爹了,以前只搞过水平的间断线,只要通过shape也可以简单的画出来,但是千万记得把hardwareAccelerated给关了,不然水平的虚线还是会是实线,这个以前也是我趟过的一个坑.那咋办呢,找不到原生的解决方案,不就是一条简单的间断线嘛,还能难住我等程序猿?这个时候,首先联想到的就是自绘控件了. 自绘控件,顾名思义就是控件所展示的内容都是我们自己绘制上去的.所有的绘制操作就是在onDraw()方法里面进行的,当然我们的这个自定

标准控件的使用技巧与自绘控件

在您决定开发 Windows 提供的常规免费自定义控件范围之外的控件之后,您必需确定自己的控件将有多少独到之处 - 在功能和外观两方面.例如,我们假定您正在创建一个类似于计速表的控件.由于公共控件库 (ComCtrl32.dll) 中没有类似的控件,您完全需要自己进行以下操作:编写所有控件功能需要的代码,进行绘制,默认终端用户的交互,以及控件与其父窗口之间需要的任意消息处理. (#add 两方面,公共控件库中没有类似的 完全重写;  只想调整公共控件功能,则可以部分修改) 另一方面,还包括一些您

Android UI开发专题(四) View自绘控件

  很多时候想要设计漂亮的Android UI,使用Android自带的控件无法满足我们的需要就要考虑自绘控件,在Android界面显示类View,可以通过继承扩展重写相关方法来实现我们的图形绘制. 首先我们需要了解下View类的底层实现,在SDK中我们可以看到View直接继承于Java的基类Object,实现了图形绘制和按键事件Drawable.Callback KeyEvent.Callback的相关方法,我们自绘时主要实现其内部的onDraw方法,相关的界面计算可以重写onMeasure方

VB 6.0 菜单条控件的增改方法

问题描述 VB 6.0 菜单条控件的增改方法 VB 6.0 在主菜单条的右边添加自己的控件.这个具体怎么实现呢?怎么添加一个超链接控件? 解决方案 这个可以通过重绘窗体来实现,将menubar放在coolbar里面