zoukankan      html  css  js  c++  java
  • 自动记录式窗口类

    有时,需要创建一个自定义窗口类。通常,您通过AfxRegisterWindowClass来完成此操作,给窗口一个您选择的类名,然后在Create调用中使用这个类。这个类通常有一个与之关联的自定义MFC子类。左边的插图显示了一个带有自定义控件——罗盘的小应用程序。 一个典型的例子可能是希望创建一个具有自定义图形的简单控件。对于这个示例,我创建了compass控件,它的类将是CCompass,它将显示一个模拟的罗盘指针。它是“泛型CWnd”的子类。 要创建这个类,进入ClassWizard,选择“添加类”按钮,然后选择“New class”选项。键入您的类的名称,并在“基类”框中选择“generic CWnd”选项,它几乎出现在选项的底部。 当您单击OK时,您将得到两个文件,指南针.cpp和指南针。h,它实现你的类。 当你回到ClassWizard中时,这个类应该被选择为你想要的类。对于自定义图形类,通常需要添加WM_ERASEBKGND和WM_PAINT处理程序。为此,在窗口中选择类,选择WM_ERASEBKGND,单击添加函数,选择WM_PAINT,然后单击添加函数。您应该得到如下所示的结果: 此时,您可以进入并填充这两个函数。 但是,在对话框中使用这个类有一个问题。您必须首先在一个特定的类名下注册“窗口类”,这样对话框编辑器才能创建它。如果您想在cdialog派生类、cpropertypage派生类或cformview派生类中使用控件,这是必要的。这意味着您必须提供一个调用来注册类,并且必须在尝试创建包含控件的类之前执行此调用。 这是不方便的。为什么程序员必须记住这样做;如果不这样做,对话框就不会出现。 我决定,在编写客户想要使用的类时,他们不应该因为必须记住注册类或理解AfxRegisterClass调用的细节而感到不便。所以我决定创建一个自动注册类的机制。 该技术是创建类的静态成员变量并对其进行初始化。作为副作用,初始化将注册类。因为该变量是一个静态成员变量,所以它将在应用程序启动时被初始化。因此,类将自动注册。 因此,我向CCompass类添加了以下声明:复制Code

    protected:
        static BOOL hasclass;
        static BOOL RegisterMe();
    #define COMPASS_CLASS_NAME _T("Compass")

    然后在CCompass.cpp文件中,我添加了:Hide  复制Code

    BOOL CCompass::hasclass = CCompass::RegisterMe();

    注意,因为这是一个静态初始化器,所以它将在系统启动时执行。这意味着由RegisterMe注册的类将在应用程序初始化时注册。然后,该类将可用于任何对话框、属性页或表单视图。 然而,有些方法是行不通的。例如,您不能使用AfxRegisterWndClass,因为它返回合成的类名的字符串,一个在执行时确定的名称,但是对话框模板要求您在构造模板时知道类名。确定AfxRegisterWndClass返回的字符串并指定它作为程序员应该使用的类名,这将是极其愚蠢的。 此外,您不能调用AfxGetInstanceHandle来获得注册类的实例句柄。这是因为AfxGetInstanceHandle使用的变量是在调用MFC的WinMain之后初始化的,而WinMain是在初始化静态成员变量之后。但是您可以使用低级API调用::GetModuleHandle。为了与16位Windows兼容,这将返回类型HMODULE而不是HINSTANCE,尽管这种区别在Win32中没有意义。但是,您必须进行显式强制转换,否则编译器会不高兴。 我还发现,如果你选择::DefWindowProc作为窗口过程,而不是NULL(当你子类化窗口时,这个将最终被AfxWndProc所取代),它会工作得更好。不要选择AfxWndProc! 在下面的代码中,我还做了一些随意的选择。例如,因为这将是一个子控件,所以它不需要图标,因此hIcon成员被设置为NULL。为了说明如何选择背景刷,如果你需要一个,我选择使用一个标准的背景颜色,对话框背景,COLOR_BTNFACE,和完全依照特殊要求的窗口类(它将有很大的意义,例如,不允许COLOR_color 0的整数指示器,但是这需要精心设计),我要的颜色加1。由于它是子控件,所以它没有菜单,因此lpszMenuName为NULL。关键参数是类名。这是程序员必须在对话框模板。隐藏,复制Code

    BOOL CCompass::RegisterMe()
       {
        WNDCLASS wc;   
        wc.style = 0;                                                 
        wc.lpfnWndProc = ::DefWindowProc; // must be this value
        wc.cbClsExtra = 0;                         
        wc.cbWndExtra = 0;                               
        wc.hInstance = (HINSTANCE)::GetModuleHandle(NULL);        
        wc.hIcon = NULL;     // child window has no icon         
        wc.hCursor = NULL;   // we use OnSetCursor                  
        wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);                
        wc.lpszMenuName = NULL;  // no menu                             
        wc.lpszClassName = COMPASS_CLASS_NAME;                          
        return AfxRegisterClass(&wc);
       }

    要在对话框中放置控件,请打开对话框编辑器。对于步骤1,选择工具箱中的“自定义控件”图标,图标,并将控件放置在对话框所需的部分,如步骤2所示。然后打开属性框。在步骤3中,删除标题,在步骤4中,键入用作COMPASS_CLASS_NAME的类的名称。 不幸的是,ClassWizard相当原始;它不会承认这种控制的存在。为什么?问微软,我不知道为什么它会从它的控件列表中排除这个控件,你可以为它创建一个成员变量。但它确实。 所以你必须“手工”编辑你的对话框。好消息是,这很容易。 例如,在对话框的头文件中找到AFX_DATA部分。我的对话框类叫做CController,并且我已经使用ClassWizard为被跟踪对象的范围、速度和高度创建了成员变量。隐藏,复制Code

    //{{AFX_DATA(CController)
        enum { IDD = IDD_CONTROLLER };
        CStatic    c_Range;
        CStatic    c_Speed;
        CStatic    c_Altitude;
        CCompass c_Compass;
        //}}AFX_DATA

    这里需要注意的是,一旦你添加了变量,ClassWizard会很乐意处理它,只是它不会让你添加变量!奇怪! 现在进入对话框的实现文件,找到DoDataExchange方法。在AFX_DATA_MAP部分中,添加如下所示的行。注意,除了控制ID和变量名反映所需的映射之外,它在形式上与其他创建控制变量的行相同。同样,一旦完成了这些操作,ClassWizard就很乐意管理控件了。隐藏,复制Code

    void CController::DoDataExchange(CDataExchange* pDX)
    {
        CFormView::DoDataExchange(pDX);
        //{{AFX_DATA_MAP(CController)
        DDX_Control(pDX, IDC_RANGE, c_Range);
        DDX_Control(pDX, IDC_SPEED, c_Speed);
        DDX_Control(pDX, IDC_ALTITUDE, c_Altitude);
        DDX_Control(pDX, IDC_COMPASS, c_Compass);
        //}}AFX_DATA_MAP
    }

    此时,您可以自由实例化对话框了。请注意,当应用程序加载时,类是注册的,因此即使您要在SDI应用程序中使用CFormView,您也不需要进一步努力来使用该类。 从GDI的角度来看,这个控件有一些有趣的属性。例如,我想要一个圆形的罗盘在控件中,但我不想约束对话框的设计者来选择一个正方形对话框。我也不希望在背景重绘时指南针内出现任何恼人的闪光。 为此,我创建了一个圆形区域,它阻止默认的WM_ERASEBKGND处理程序触摸控件的内容。然后我使用这个限制剪辑的输出操作内的罗盘上升。这也可以用于点击测试,使用PtInRegion查看鼠标是否在圆形区域。 罗盘的禁用和启用模式如下所示。 CCompass: CreateClipRegionHide,复制Code

    CRect CCompass::<A name=CreateClipRegion>CreateClipRegion</A>(CRgn & rgn)
        {
         CRect r;
         GetClientRect(&r);
         int radius = min(r.Width() / 2, r.Height() / 2);
         CPoint center(r.Width() / 2, r.Height() / 2);
         rgn.CreateEllipticRgn(center.x - radius, center.y - radius,
              center.x + radius, center.y + radius);
         return CRect(center.x - radius, center.y - radius,
              center.x + radius, center.y + radius);
        } // CCompass::CreateClipRegion

    CCompass: OnEraseBkgndHide,复制Code

    BOOL CCompass::<A name=OnEraseBkgnd>OnEraseBkgnd</A>(CDC* pDC) 
       {
        CRgn rgn;
        CSaveDC sdc(pDC);
        <A href="#CreateClipRegion">CreateClipRegion</A>(rgn);
        pDC->SelectClipRgn(&rgn, RGN_DIFF); // remove circle from update area
        return CWnd::OnEraseBkgnd(pDC);
       }

    CCompass: MapDC 由于我映射DC的频率不同,为此我创建了一个单独的方法。隐藏,复制Code

    void CCompass::<A name=MapDC>MapDC</A>(CDC & dc)
        {
         dc.SetMapMode(MM_ISOTROPIC);
         CRect r;
         GetClientRect(&r);
         dc.SetWindowExt(r.Width(), r.Height());
         dc.SetViewportExt(r.Width(), -r.Height());
         CPoint center(r.left + r.Width() / 2, r.top + r.Height() / 2);
         dc.SetViewportOrg(center.x, center.y);
        } // CCompass::MapDC

    CDoublePoint 这个类让我来表示分数角。事实证明,应用程序中的其他表示已经使用了双精度,因此在compass中使用它是一种自然的扩展。注意简单的CPoint转换,它截断而不是舍入;对于应用程序来说,这已经足够了。隐藏,复制Code

    class <A name=CDoublePoint>CDoublePoint</A> {
        public:
           CDoublePoint(){}
           CDoublePoint(double ix, double iy) {x = ix; y = iy; }
           double x;
           double y;
           operator CPoint() { CPoint pt; pt.x = (int)x; pt.y = (int)y; return pt; }
    };

    CCompass: OnLButtonDown 只有当鼠标在指南针区域时,才会响应按钮检测。注意,我向父节点发送了一条用户定义的消息,这在我的同伴文章中有描述。隐藏,复制Code

    void CCompass::OnLButtonDown(UINT nFlags, CPoint point) 
       {
        CRgn rgn;
        <A href="#CreateClipRegion">CreateClipRegion</A>(rgn);
        if(rgn.<A name=PtInRegion>PtInRegion</A>(point))
           { /* in region */
        CClientDC dc(this);
        <A href="#MapDC">MapDC</A>(dc);
        dc.DPtoLP(&point);
        GetParent()->SendMessage(CPM_CLICK, (WPARAM)point.x, (LPARAM)point.y);
        return;
           } /* in region */
        CWnd::OnLButtonDown(nFlags, point);
       }

    DegreesToRadians / GeographicToGeometric 我有一个实用函数,转换角度弧度,声明在一个单独的头文件。隐藏,复制Code

    __inline double <A name=DegreesToRadians>DegreesToRadians</A>(double x) 
      { return (((x)/360.0) * (2.0 * 3.1415926535)); }

    法向几何坐标系的角度0.0指向原点的右侧,并随着角度的增加逆时针旋转。我们想从地理的角度来考虑度数,0.0是北,90.0是东,180.0是南,270.0是西。下面的内联方法对于从地理的自然坐标转换为math.h库所需的坐标非常有用。隐藏,复制Code

    __inline double <A name=GeographicToGeometric>GeographicToGeometric</A>(double x) { return -(x - 90.0); }

    CCompass: CCompass 构造函数加载坐标指示器表。隐藏,复制Code

    CCompass::CCompass()
    {
     // Note: for optimal performance, sort monotonically by font size
     // Note: The first entry must be the largest 
     display.Add(new displayinfo(  0.0, _T("N"), 100.0, TRUE));
     display.Add(new displayinfo( 90.0, _T("E"), 90.0, FALSE));
     display.Add(new displayinfo(180.0, _T("S"), 90.0, FALSE));
     display.Add(new displayinfo(270.0, _T("W"), 90.0, FALSE));
     display.Add(new displayinfo( 45.0, _T("NE"), 80.0, FALSE));
     display.Add(new displayinfo(135.0, _T("SE"), 80.0, FALSE));
     display.Add(new displayinfo(225.0, _T("SW"), 80.0, FALSE));
     display.Add(new displayinfo(315.0, _T("NW"), 80.0, FALSE));
    
     RegistryString compass(IDS_COMPASS);
     compass.load();
     if(compass.value.GetLength() == 0 || !arrow.Read(compass.value))
        arrow.Read(_T("Arrow.plt")); // use default
    
     angle = 0.0; // initialize at North
     ArrowVisible = FALSE;
    }

    CCompass: OnPaintHide,收缩,复制Code

    void CCompass::OnPaint() 
       {
        CPaintDC dc(this); // device context for painting
        CBrush br(::GetSysColor(COLOR_INFOBK));
        CRgn rgn;
        CRect r;
        r = <A href="#CreateClipRegion">CreateClipRegion</A>(rgn);
    #define BORDER_WIDTH 2
        CPen border(PS_SOLID, BORDER_WIDTH, RGB(0,0,0));
        CBrush needle(RGB(255, 0, 0));
    
    #define ENABLED_COLOR RGB(0,0,0)
    #define DISABLED_COLOR RGB(128,128,128)
    
        CPen enabledPen(PS_SOLID, 0, ENABLED_COLOR);
        CPen disabledPen(PS_SOLID, 0, DISABLED_COLOR);
        //----------------------------------------------------------------
        // GDI resources must be declared above this line
        //----------------------------------------------------------------
        CSaveDC sdc(dc);
        dc.SelectClipRgn(&rgn); // clip to compass
        dc.FillRgn(&rgn, &br);
        // Convert the origin to the center of the circle
        CPoint center(r.left + r.Width() / 2, r.top + r.Height() / 2);
        // Renormalize the rectangle to the center of the circle
        r -= center;
        int radius = r.Width() / 2;
        dc.SetBkMode(TRANSPARENT);
        <A href="#MapDC">MapDC</A>(dc);
    
        // Draw the border
        { 
         CSaveDC sdc2(dc);
         dc.SelectClipRgn(NULL);
         dc.SelectStockObject(HOLLOW_BRUSH);
    
         dc.SelectObject(&border);
         dc.Ellipse(-radius, -radius, radius, radius);
    
         r.InflateRect(-BORDER_WIDTH, -BORDER_WIDTH);
         radius = r.Width() / 2;
        }
        radius = r.Width() / 2;
        
        dc.SelectObject(IsWindowEnabled() ? &enabledPen : &disabledPen);
        // Draw N-S line
        dc.MoveTo(0, radius);
        dc.LineTo(0, -radius);
        // Draw E-W line
        dc.MoveTo(-radius, 0);
        dc.LineTo(radius, 0);
    
        // Draw SW-NE line
        dc.MoveTo((int)(radius * sin(<A href="#DegreesToRadians">DegreesToRadians</A>(<A href="#GeographicToGeometric">GeographicToGeometric</A>(225.0)))), 
          (int)(radius * cos(<A href="#DegreesToRadians">DegreesToRadians</A>(<A href="#GeographicToGeometric">GeographicToGeometric</A>(225.0)))) );
        dc.LineTo((int)(radius * sin(<A href="#DegreesToRadians">DegreesToRadians</A>(<A href="#GeographicToGeometric">GeographicToGeometric</A>( 45.0)))),
          (int)(radius * cos(<A href="#DegreesToRadians">DegreesToRadians</A>(<A href="#GeographicToGeometric">GeographicToGeometric</A>( 45.0)))) );
        // Draw NW-SE line
        dc.MoveTo((int)(radius * sin(<A href="#DegreesToRadians">DegreesToRadians</A>(<A href="#GeographicToGeometric">GeographicToGeometric</A>(315.0)))), 
          (int)(radius * cos(<A href="#DegreesToRadians">DegreesToRadians</A>(<A href="#GeographicToGeometric">GeographicToGeometric</A>(315.0)))) );
        dc.LineTo((int)(radius * sin(<A href="#DegreesToRadians">DegreesToRadians</A>(<A href="#GeographicToGeometric">GeographicToGeometric</A>(135.0)))),
          (int)(radius * cos(<A href="#DegreesToRadians">DegreesToRadians</A>(<A href="#GeographicToGeometric">GeographicToGeometric</A>(135.0)))) );
    
        // Now create the font elements
        // The symbols are placed along a circle which is inscribed
        // within the compass area
        //
        // +-----------------------------+
        //         /     N     <IMG height=120 src="/KB/dialog/selfregister/compassdisabled.gif" width=120 align=right border=0>
        //        / NW   |   NE 
        //       /       |       
        //       |       |       |
        //       | W-----+-----E |  
        //       |       |       |
        //              |       /
        //         SW   |   SE /
        //              S     /
        // +-----------------------------+
    
        double size = 0.15 * (double)r.Width();
        double CurrentFontSize = 0.0; // current font size
        CFont * f = NULL;
        dc.SetTextColor(IsWindowEnabled() ? ENABLED_COLOR : DISABLED_COLOR);
    
        for(int i = 0; i < display.GetSize(); i++)
           { /* draw points */
        CSaveDC sdc2(dc);
        dc.SetBkMode(OPAQUE);
        dc.SetBkColor(::GetSysColor(COLOR_INFOBK));
        if(display[i]->GetSize() != CurrentFontSize)
           { /* new font */
            if(f != NULL)
               delete f;
            f = display[i]->CreateFont(size, _T("Times New Roman"));
           } /* new font */
        dc.SelectObject(f);
        CurrentFontSize = display[i]->GetSize();
        CString text = display[i]->GetText();
        //
        //      4 | 1
        //      --+--
        //      3 | 2
        //------------------------------------------------------------------
        //  Ø    qdant    x     y   x-origin     y-origin    alignment
        //  ----------------------------------------------------------------
        //  0    4.1      0    >0   x-w/2        y           TOP, LEFT
        //  <90  1       >0    >0   x            y           TOP, RIGHT
        //  90   1.2     >0    0    x            y-h/2       TOP, RIGHT
        //  <180 2       >0    <0   x            y           BOTTOM, RIGHT
        //  180  2.3     0     <0   x-w/2        y           BOTTOM, RIGHT
        //  <270 3       <0    <0   x            y           BOTTOM, LEFT
        //  270  3.4     <0    0    x            y-h/2       TOP, LEFT
        //  <360 4       <0    >0   x            y           TOP, LEFT
    
        int x = (int)(radius * 
          cos(<A href="#DegreesToRadians">DegreesToRadians</A>(<A href="#GeographicToGeometric">GeographicToGeometric</A>(display[i]->GetAngle()))));
        int y = (int)(radius * 
          sin(<A href="#DegreesToRadians">DegreesToRadians</A>(<A href="#GeographicToGeometric">GeographicToGeometric</A>(display[i]->GetAngle()))));
        CSize textSize = dc.GetTextExtent(text);
    
        double theta = display[i]->GetAngle();
        if(theta == 0.0)
           { /* 0 */
            dc.SetTextAlign(TA_TOP | TA_LEFT);
            x -= textSize.cx / 2;
           } /* 0 */
        else
        if(theta < 90.0)
           { /* < 90 */
            dc.SetTextAlign(TA_TOP | TA_RIGHT);
           } /* < 90 */
        else
        if(theta == 90.0)
           { /* 90 */
            dc.SetTextAlign(TA_TOP | TA_RIGHT);
            y += textSize.cy / 2;
           } /* 90 */
        else
        if(theta < 180.0)
           { /* < 180 */
            dc.SetTextAlign(TA_BOTTOM | TA_RIGHT);
           } /* < 180 */
        else
        if(theta == 180.0)
           { /* 180 */
            dc.SetTextAlign(TA_BOTTOM | TA_LEFT);
            x -= textSize.cx / 2;
           } /* 180 */
        else
        if(theta < 270.0)
           { /* < 270 */
            dc.SetTextAlign(TA_BOTTOM | TA_LEFT);
           } /* < 270 */
        else
        if(theta == 270)
           { /* 270 */
            dc.SetTextAlign(TA_TOP | TA_LEFT);
            y += textSize.cy / 2;
           } /* 270 */
        else
           { /* < 360 */
            dc.SetTextAlign(TA_TOP | TA_LEFT);
           } /* < 360 */
        dc.TextOut(x, y, text);
           } /* draw points */
        if(f != NULL)
           delete f;
    
        // Draw the arrow
        if(IsWindowEnabled() && ArrowVisible)
           { /* draw arrow */
            CRect bb = arrow.GetInputBB();
        dc.SelectObject(&needle);
        arrow.Transform(angle, (double)abs(bb.Height()) / (2.0 * (double)radius));
        arrow.Draw(dc, <A href="#CDoublePoint">CDoublePoint</A>(0.0, 0.0));
           } /* draw arrow */
    
        // Do not call CWnd::OnPaint() for painting messages
       }

    本文转载于:http://www.diyabc.com/frontweb/news12152.html

  • 相关阅读:
    POJ 1795 DNA Laboratory
    CodeForces 303B Rectangle Puzzle II
    HDU 2197 本源串
    HDU 5965 扫雷
    POJ 3099 Go Go Gorelians
    CodeForces 762D Maximum path
    CodeForces 731C Socks
    HDU 1231 最大连续子序列
    HDU 5650 so easy
    大话接口隐私与安全 转载
  • 原文地址:https://www.cnblogs.com/Dincat/p/13473608.html
Copyright © 2011-2022 走看看