• 堆叠Windows控制教程


    介绍 从头开始开发自定义控件通常是不必要的,因为标准工具集非常全面,如果还不够,子类化或所有者自己绘制的风格就可以完成这项工作。这一点很重要,不容忽视。当从头开始开发自定义控件时,结果很有可能会低于标准。 也就是说,有一些控件完全丢失了,如果我们想在应用程序中部署它们,除了凭空构建它们之外,没有其他解决方案。其中一个例子就是Spybot或Outlook使用的“堆叠窗口控件”(或者随便叫什么名字)。因为它不在标准控件中,而且它是一个有趣的练习,所以本教程解释了如何一步一步地开发这种类型的控件。 本教程的目标读者是新手程序员,在开始之前,我想建议您不要阅读本文,而是尝试自己开发控件。虽然它可能看起来令人生畏,或者您可能不知道从哪里开始,但它并不像您可能认为的那么难。试一试,看看你能走多远,然后回来看看我要说什么。提示:这一切都是关于窗口的大小调整和重新定位,没有别的。 要完成什么 目标是一个“堆叠窗口控件”。就是这样。它将尽可能地通用,并将说明如何组装此类控件。 热心的读者可能想知道,我在编写演示项目的同时也编写了本教程。下面的说明、解释和代码相当于上面屏幕截图(准确地说是左边的那个)中堆叠窗口控件的开发。 继续编码。 循序渐进的过程 项目启动 设置很简单。创建一个新的基于对话框的项目,并将警告级别设置为4(项目设置,C/ c++选项卡)。四级将确保我们注意到任何可疑的信息,这样我们就可以决定如何处理“信息警告,在大多数情况下,可以安全地忽略”(来自文档)。 让我们开始处理控件。创建一个新的名为CStackedWndCtrl的MFC类,它使用CStatic作为基类。 在资源编辑器中,添加一个ID为IDC_SWC的图片控件。保留默认的框架为类型,黑色为颜色。 使用MFC ClassWizard,向IDC_SWC添加一个名为m_StackedWndCtrl的成员变量,确保选择Control作为类别,CStackedWndCtrl作为变量类型。 在单击OK时,一个消息框警告我们确保在对话框代码中包含了类CStackedWndCtrl的头文件。如果你还没有做过,现在就做。 数据结构 任何类型的控件的主干都是一个数据结构,用于保存将要显示的信息。 会显示什么呢?该控件由窗格组成,每个窗格包含两个窗口,一个rubric窗口和一个content窗口。下图说明了这个概念。 该控件的机制要求一次只显示一个窗格的内容窗口。单击窗格的红字窗口将触发其相关内容窗口的显示,还将隐藏当前显示的窗格的内容窗口。 因此,该数据结构将包含一对指向CWnd对象的指针和一个布尔标志,用于指示是显示还是隐藏窗格的内容窗口。不需要其他东西。隐藏,复制Code

    #include <afxtempl.h>
    
    class CStackedWndCtrl : public CStatic
    {
      ....
      ....
    
    // Attributes
    protected:
    
      typedef struct
      {
          CWnd* m_pwndRubric;
          CWnd* m_pwndContent;
          BOOL  m_bOpen;
      } TDS_PANE, *PTDS_PANE;
    
      CArray<PTDS_PANE, PTDS_PANE> m_arrPanes;
    
      ....
      ....
    }

    数组是存储、检索和使用这些结构的一种方便而充分的方法。请记住,为了使用数组模板,我们需要包括适当的标题。 下一个任务是编写一个公共方法,该方法允许我们向控件添加窗格。什么也没有做。我们复制作为参数传递的窗口对象的指针,并将新窗格设置为所显示的窗格。隐藏,收缩,复制Code

    int CStackedWndCtrl::AddPane( CWnd* pwndRubric, CWnd* pwndContent )
    {
      // Hide whatever pane's content window is currently shown
      // We will always show the content window of the last pane added
      for( int i = 0; i < m_arrPanes.GetSize(); i++ )
          if( m_arrPanes[ i ]->m_bOpen )
              m_arrPanes[ i ]->m_bOpen = FALSE;
    
      // Create a new pane structure
      PTDS_PANE pPane = new TDS_PANE;
    
      if( pPane == NULL )
      {
          AfxMessageBox( "Failed to add a new pane to" 
                         " the stack.
    
    Out of memory." );
          return -1;
      }
    
      // Copy the pointers to the rubric and content windows
      // Also, set this pane as open
      pPane->m_pwndRubric     = pwndRubric;
      pPane->m_pwndContent    = pwndContent;
      pPane->m_bOpen          = TRUE;
    
      // Add the new pane to the end of the stack
      int iIndex = m_arrPanes.Add( pPane );
    
      // Rearrange the stack
      RearrangeStack();
    
      // Return the index of the new pane
      return iIndex;
    }

    在我们考虑安排和显示窗格之前(如果您想测试代码,只需注释掉对RearrangeStack方法的调用),确保该结构在退出时被正确删除是非常重要的,以防止内存泄漏。我们在析构函数中执行这个任务,如下所示:复制Code

    CStackedWndCtrl::~CStackedWndCtrl()
    {
      for( int i = 0; i < m_arrPanes.GetSize(); i++ )
      {
          // Delete the rubric window
          m_arrPanes[ i ]->m_pwndRubric->DestroyWindow();
    
          delete m_arrPanes[ i ]->m_pwndRubric;
    
          // Delete the content window
          m_arrPanes[ i ]->m_pwndContent->DestroyWindow();
    
          delete m_arrPanes[ i ]->m_pwndContent;
    
          // Delete structure
          delete m_arrPanes[ i ];
      }
    
      m_arrPanes.RemoveAll();
    }

    简单的东西。我们循环遍历窗格数组,销毁每个窗口,然后删除每个窗口对象,然后删除每个窗格对象,最后删除数组中的所有指针。 这个功能足以使CStackedWndCtrl类能够完成它的工作。我们可以添加窗格,当控件被销毁时,这些窗格将被正确地处理。 视觉魔术 恐怕一点也不。安排和显示控件的算法非常简单。 我们循环遍历窗格,通过一个预定的度量来抵消帧的顶部,m_iRubricHeight,它已经在演示中设置了一个默认值(可以自由地进行实验)。当我们点击的窗格是开放的,我们使用的数量标题左显示窗口,计算此窗格的内容窗口的尺寸。查看代码。隐藏,收缩,复制Code

    void CStackedWndCtrl::RearrangeStack()
    {
      CRect rFrame;
    
      GetClientRect( &rFrame );
    
      for( int i = 0; i < m_arrPanes.GetSize(); i++ )
      {
          // Rubric windows are always visible
          m_arrPanes[ i ]->m_pwndRubric->SetWindowPos( NULL,
                                                       0,
                                                       rFrame.top,
                                                       rFrame.Width(),
                                                       m_iRubricHeight,
                                                       SWP_NOZORDER | SWP_SHOWWINDOW );
    
          // Only the content window of the flagged pane is shown
          // All others are hidden if they aren't already
          if( m_arrPanes[ i ]->m_bOpen )
          {
              // From the bottom of the frame, take off as many rubric
              // window's heights as there are left to display
              int iContentWndBottom = rFrame.bottom - 
                  ( ( m_arrPanes.GetSize() - i ) * m_iRubricHeight );
    
              m_arrPanes[ i ]->m_pwndContent->SetWindowPos(
                               NULL,
                               0,
                               rFrame.top + m_iRubricHeight,
                               rFrame.Width(),
                               iContentWndBottom - rFrame.top,
                               SWP_NOZORDER | SWP_SHOWWINDOW );
    
              // The next rubric window will be placed right below
              // this pane's content window
              rFrame.top = iContentWndBottom;
          }
          else
              m_arrPanes[ i ]->m_pwndContent->ShowWindow( SW_HIDE );
    
          // The top of the frame is offset by the height of a rubric window
          rFrame.top += m_iRubricHeight;
      }
    }

    负责安排和显示控制。 现在让我们添加一个调用PreSubclassWindow摆脱周围的黑色框架控制。虽然它是有用的在使用资源编辑器时,它是不必要的,难看的,当应用程序运行。隐藏,复制Code

    void CStackedWndCtrl::PreSubclassWindow() 
    {
      // Remove the black frame and clip children to reduce flickering
      ModifyStyle( SS_BLACKFRAME, WS_CLIPCHILDREN );
    
      CStatic::PreSubclassWindow();
    }

    我们也借此机会增加WS_CLIPCHILDREN国旗减少闪烁的调整控制时,这提醒了我…… …它总是一个好主意,以确保控制能够在必要时调整自己。在这种情况下,功能很容易实现。火Classwizard,弹出添加消息处理程序,RearrangeStack打电话。隐藏,复制Code

    void CStackedWndCtrl::OnSize(UINT nType, int cx, int cy) 
    {
      CStatic::OnSize(nType, cx, cy);
    
      RearrangeStack();
    }

    我们几乎已经完成了。如果你添加一些测试窗格、编译和运行;栈控制将显示所有窗口标题windows最后窗格的内容。 当然,控制不能响应用户点击标题窗口。我们还没有为它编写的代码。是我们的下一个和最后一个任务列表。 唯一的要求的窗口标题 只要我们控制而言,标题和内容窗口可以是任何类型的窗口。字面上。对话框、静态控件、列表框控件、树控件,日历控件,编辑/ richedit控件,通用的窗户,甚至自定义控件。如果我们能得到一个CWnd指针指向它,类CStackedWndCtrl将按预期工作。唯一的限制是常识,而不是一个技术问题。举个例子,一个组合框可以设置为标题或内容窗口,但其适当性相当可疑。 然而,有一个要求,它适用于标题窗口。点击时,必须告知其母(CStackedWndCtrl对象),这样窗口可以显示相关内容。我们将完成这个通过发送一条消息。 为简单起见,我将使用按钮标题窗口。毕竟,他们是最明智的选择。我们将得到一个从CButton类,并添加这个专门的功能。 那么,创建一个名为CTelltaleButton来自CButton类。添加以下消息定义它的头,一个消息处理程序= BN_CLICKED(反映消息)。隐藏,复制Code

    // In TelltaleButton.h
    
    #define WM_RUBRIC_WND_CLICKED_ON ( WM_APP + 04100 )
    
    // In TelltaleButton.cpp
    
    void CTelltaleButton::OnClicked() 
    {
      GetParent()->SendMessage( WM_BUTTON_CLICKED, (WPARAM)this->m_hWnd );
    }

    将发送一个消息,其中包含标题窗口,按钮,自己处理。根据这些信息,母公司控制能够找出哪些标题窗口点击。 现在,我们在CStackedWndCtrl处理消息通过手动添加消息映射的方法如下:隐藏,收缩,复制Code

    // In StackedWndCtrl.h
    
    #define WM_RUBRIC_WND_CLICKED_ON ( WM_APP + 04100 )
    
      ...
      ...
    
      // Generated message map functions
    protected:
      //{{AFX_MSG(CStackedWndCtrl)
      afx_msg void OnSize(UINT nType, int cx, int cy);
      //}}AFX_MSG
      afx_msg LRESULT OnRubricWndClicked(WPARAM wParam, LPARAM lParam);
      DECLARE_MESSAGE_MAP()
    
    // In StackedWndCtrl.cpp
    
      ...
      ...
    
    
    BEGIN_MESSAGE_MAP(CStackedWndCtrl, CStatic)
      //{{AFX_MSG_MAP(CStackedWndCtrl)
      ON_WM_SIZE()
      //}}AFX_MSG_MAP
      ON_MESSAGE(WM_RUBRIC_WND_CLICKED_ON, OnRubricWndClicked)
    END_MESSAGE_MAP()
    
      ...
      ...
    
    LRESULT CStackedWndCtrl::OnRubricWndClicked(WPARAM wParam, LPARAM /*lParam*/)
    {
      HWND hwndRubric = (HWND)wParam;
      BOOL bRearrange = FALSE;
    
      for( int i = 0; i < m_arrPanes.GetSize(); i++ )
        if( m_arrPanes[ i ]->m_pwndRubric->m_hWnd == hwndRubric )
        {
          // Rearrange the control only if a rubric window
          // other than the one belonging to the pane that
          // is currently open is clicked on
          if( m_arrPanes[ i ]->m_bOpen == FALSE )
          {
            m_arrPanes[ i ]->m_bOpen = TRUE;
            bRearrange = TRUE;
          }
        }
        else
          m_arrPanes[ i ]->m_bOpen = FALSE;
    
      if( bRearrange )
        RearrangeStack();
    
      // In case the rubric window that has sent the message wants to know
      // if the control has been rearranged, return the flag
      return bRearrange;
    }

    所有这一切都归结到遍历窗格,以便找到被点击的标题窗口。如果是不同的,属于当前打开面板,重新排列控制。 一些华而不实 因为CStackedWndCtrl非常灵活,可以用于其标题和内容窗口,很容易爵士乐。为了说明如何做到这一点,我已经包含在演示项目一个“普通”的控制和使用委员会的大卫。Calabro阴影按钮和Everaldo科埃略的图标。如您所见,通过检查代码演示,而不是一行代码在CStackedWndCtrl需要修改。,因为它应该。 我们的短旅程结束,我的朋友;我走这条路,你走。我希望我展示了你的风景,种子你的想象力,并且我们的安静的交易将会有利于你。 反馈 我的意图是提供一个教程,是编码显然,尽可能简单的理解和遵循。我相信有更好的解决方案,我在这里实现的功能。任何改进的建议,简化,或更好的解释代码是受欢迎的。 致谢 演示项目,我使用一个旧版本的CResizableDialog Paolo梅西纳时我已经喜欢写文章的代码项目。谢谢保罗。 另一个意大利的工作委员会的大卫。Calabro CButtonST,被用于演示项目。谢谢大卫。。 我用Everaldo科埃略的一些图标在演示项目。你可以找到更多他的作品。Everaldo表示感谢。 我还用丹造型的视觉检漏仪来检查内存的恶作剧。一个非常方便的工具,我推荐给所有人。谢谢丹。 最后,我想表达我的感激之情,每个人都分享,或可以自由地分享知识。一次又一次,我看到的人类同胞写文章,教程,帮助陌生人的论坛,和我谦卑,出于他们的慷慨。很高兴能够回馈。 本文转载于:http://www.diyabc.com/frontweb/news4969.html

  • 相关阅读:
    Jmeter(十三)用Jmeter自带录制工具代理录制手机端应用脚本APP脚本
    AppScan 扫描测试策略
    AppScan 工作原理
    AppScan扫描结果分析及工具栏使用
    Java学习之正则表达式
    Java学习之反射练习
    Java学习之反射
    Java学习之网络编程(TCP协议实例)
    Java学习之网络编程(UDP协议实例)
    Java学习之IO(编码练习--截取字符串根据字节)
  • 原文地址:https://www.cnblogs.com/Dincat/p/13462093.html
Copyright © 2020-2023  润新知