在这个颜值当道,屌丝闪边的时代,拼不过颜值拼内涵,只有知识丰富才能提升一个人的内在气质和修养,所谓人丑就要多学习,今天图老师给大家分享vc层叠式窗体控件指南,希望可以对大家能有小小的帮助。
【 tulaoshi.com - 编程语言 】
介绍
从零开发自定义控件常常是不需要的,因为标准控件组是相当全面的,如果不够用,子类化或自绘等方法就可以搞掂这个工作。这是一个不应被忽略的要点。在从零开发一个自定义控件时,千辛万苦获得的控件往往会不如标准(控件)。
(本文来源于图老师网站,更多请访问http://www.tulaoshi.com/bianchengyuyan/)那就是说,这里只有少数真地缺少的控件,如果我们想要在我们的应用程序中部署它们,除了无中生有地构建它们别无他法。有一个这样的情况就是名称为“层叠式窗体控件”,或无论什么它的其他称呼,例如:Spybot或Outlook。因为它不在标准控件之中并且因为它是一个有趣的练习,本指南讲解了如何开发这类控件,并一步一步地给予讲解。
本指南的目标读者为程序员新手,在开始之前,我想挑战你一下:即在不阅读本文的情况下先尝试自己开发这个控件。尽管这看起来可能会让人退缩或你可能不知道从哪里开始,它不是像你想像得那样难。尝试一下,看看你能走多远,这时再回来看看本指南并检验一下我所说的话。提示:它完全与窗体的重新恢复尺寸和重新复位有关,没有其他。
我们要完成什么
目标是一个“层叠式窗体控件”。就是它。它将会被尽可能地泛型化并会阐明如何聚集该类控件的一个。
热心的读者可能希望知道我在写这个演示工程时写了这个指南。下面的指导、解释和代码实际上就是在上面的截屏中的层叠窗体控件(准确地说来就是图中左边那个控件)的开发。本文发表于http://bianceng.cn(编程入门网)
让我们从代码开始。
过程详解
工程开始
创建工作是简单的。创建一个新的基于对话框的工程,并设置警告级别为4(工程设置,C/C++标签)。级别4将确保任何可疑事物给我们带来注意以使得由我们来决定要做什么“这里提示的警告在绝大多数情况下可以被安全地忽略”(此语出自文档)
让我们在该控件上开始工作。创建一个用CStatic作为基类的新的MFC类命名为CStackedWndCtrl。
在资源编辑器中,添加一个图片控件ID号为IDC_SWC。保留Type的值为缺省的Frame并将Color置为Black。
使用MFC ClassWizard添加一个数据变量到IDC_SWC命名为m_StackedWndCtrl,确保选择了Control作为Category以及CStackedWndCtrl作为变量类型。
在OK上点击,弹出一个消息框提示我们确保我们已经为类CStackedWndCtrl包含头文件在我们的对话框代码中。如果你没有包含它现在就要做了。
数据结构
任何控件的主要部分就是一个数据结构,数据结构可以保持将要显示的信息。
好的,什么将会被显示?该控件用面板制作出来,每个面板包含两个窗体,一个标题窗体和一个内容窗体。下面的图片说明了这个概念。
控件的机制要求只有一个面板的内容窗体在一个时间内显示。在一个面板上点击标题窗体将触发其相应的内容窗体显示,并且也隐藏了当前显示面板的内容窗体。
(本文来源于图老师网站,更多请访问http://www.tulaoshi.com/bianchengyuyan/)因此,数据结构将包含一对指向CWnd 对象的指针和一个布尔标识值以指出是否显示或隐藏这个面板的内容窗体。不需要任何其他的东西了。
#include afxtempl.h
class CStackedWndCtrl : public CStatic
{
....
....
// Attributes
protected:
typedef struct
{
CWnd* m_pwndRubric;
CWnd* m_pwndContent;
BOOL m_bOpen;
} TDS_PANE, *PTDS_PANE;
CArrayPTDS_PANE, PTDS_PANE m_arrPanes;
....
....
}对于这些结构的保存、检索和操作,用一个数组是一个方便的且足够的方法。记住为了使用这个数组模版,我们需要包含相应的头文件。
下一个任务是写一个允许我们添加面板到控件上的public方法。这没有什么困难。我们使窗体对象的指针作为参数传递,并设置新的面板如其所显示的一样。
int CStackedWndCtrl::AddPane( CWnd* pwndRubric, CWnd* pwndContent )
{
// 隐藏无论哪一个正在显示面板的内容窗体
//我们将总是显示最近添加的面板的内容窗体
for( int i = 0; i m_arrPanes.GetSize(); i++ )
if( m_arrPanes[ i ]-m_bOpen )
m_arrPanes[ i ]-m_bOpen = FALSE;
//创建一个新的面板结构
PTDS_PANE pPane = new TDS_PANE;
if( pPane == NULL )
{
AfxMessageBox( "Failed to add a new pane to"
" the stack.
Out of memory." );
return -1;
}
// 拷贝指针到标题和内容窗体
//同时,设置这个面板为打开状态
pPane-m_pwndRubric = pwndRubric;
pPane-m_pwndContent = pwndContent;
pPane-m_bOpen = TRUE;
// 添加该新面板到栈的尾部
int iIndex = m_arrPanes.Add( pPane );
// 重新排列栈
RearrangeStack();
// 返回新面板的索引号
return iIndex;
}
在我们担心排列和显示面板之前(如果你想要测试这个代码,只要参考RearrangeStack
方法的调用 ),我们要确保在退出时该结构体被完全删除是非常重要的,以免内存泄漏。我们在析构器中执行该任务,如下所示:
CStackedWndCtrl::~CStackedWndCtrl()
{
for( int i = 0; i m_arrPanes.GetSize(); i++ )
{
//删除标题窗体
m_arrPanes[ i ]-m_pwndRubric-DestroyWindow();
delete m_arrPanes[ i ]-m_pwndRubric;
// 删除内容窗体
m_arrPanes[ i ]-m_pwndContent-DestroyWindow();
delete m_arrPanes[ i ]-m_pwndContent;
// 删除结构体
delete m_arrPanes[ i ];
}
m_arrPanes.RemoveAll();
}
简单填充。我们遍历该面板上的数组,销毁每个窗体,然后删除每个窗体对象,然后删除每个面板对象,并且最后,从数组中移除所有指针。
这个功能足以使得CStackedWndCtrl类可以做它的工作。我们可以添加面板,同时它们(译注:指面板)在控件被销毁时被适当释放。
可视的魔力
None of it, 我想.排列和显示控件的算法是相当简单的。
我们遍历面板,通过一个预先估量消除顶部框架,m_iRubricHeight,它在演示程序中被设置为一个默认的值(可以自由测试)当我们点击打开的面板,我们用余下来要显示的标题窗体的数量来计算该面板的内容窗体的尺寸。请看下面的代码。
void CStackedWndCtrl::RearrangeStack()
{
CRect rFrame;
GetClientRect( &rFrame );
for( int i = 0; i m_arrPanes.GetSize(); i++ )
{
// 标题窗体总是显示
m_arrPanes[ i ]-m_pwndRubric-SetWindowPos(
NULL,
0,
rFrame.top,
rFrame.Width(),
m_iRubricHeight,
SWP_NOZORDER | SWP_SHOWWINDOW );
// 只有已标记面板的内容窗体被显示
// 如果它们没有准备好,所有其他的都隐藏
if( m_arrPanes[ i ]-m_bOpen )
{
// 从框架的底部,去掉余下要显示的那些标题窗体的一样高度的尺寸
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 );
//下一个标题窗体将被放置于该面板内容窗体的正下方
rFrame.top = iContentWndBottom;
}
else
m_arrPanes[ i ]-m_pwndContent-ShowWindow( SW_HIDE );
//框架的顶部偏移一个标题窗体的高度
rFrame.top += m_iRubricHeight;
}
}
以上处理了控件的排列和显示。
让我们现在添加一个调用到PreSubclassWindow以去除图片控件周围的黑框。在资源编辑器工作时这是有效的,在应用程序运行时它是不必的且难看。
void CStackedWndCtrl::PreSubclassWindow()
{
// 移除黑框并夹住子控件以避免闪烁
ModifyStyle( SS_BLACKFRAME, WS_CLIPCHILDREN );
CStatic::PreSubclassWindow();
}我们已经获得机会添加WS_CLIPCHILDREN 标志以在重新改变控件尺寸时减少闪烁,这提醒我…
…确保该控件能在需要时改变自己的尺寸大小总是一个好主意。在此情况中,该功能是相当容易实现的。调出Classwizard,为WM_SIZE添加一个消息句柄,并做一个调用到RearrangeStack。
void CStackedWndCtrl::OnSize(UINT nType, int cx, int cy)
{
CStatic::OnSize(nType, cx, cy);
RearrangeStack();
}
我们几乎已经做好了。如果你添加一些测试面板,编译并运行;这个层叠式控件将显示所有标题窗体和最后的面板的内容窗体。
当然,这个控件不会对用户点击标题窗体做出反应。我们还没有为其写响应代码啊。它是我们任务清单上的下一个也是最后一个任务了。
标题窗体的惟一需求
至于我们的控件,标题和内容窗体可以是任何一种窗体。照字面意思,可以是对话框、static控件、列表框/控件、树控件、日历控件、编辑框/ richedit控件、generic窗体、甚至自定义控件。如果我们可以获得一个指向它的CWnd指针,CStackedWndCtrl类会如预期一样工作。这里惟一的限制是常识,而不是一个技术问题。举个例子,一个组合框可能被设置为标题或内容窗体,但是其适宜性相当值得怀疑。
然而,这里有一个必要条件,同时它被应用于标题窗体。当它被点击,它必须通知其父(一个CStackedWndCtrl 对象)以使得相关内容窗体可以被显示。我们将通过发送一个消息完成这个任务。
为了简化,我将用按钮作为标题窗体。它们毕竟是绝大多数可能的选择。我们将从CButton继承一个类,并且添加这个有点特别的功能。
那么,我们现在创建一个继承于CButton的名为CTelltaleButton的类。添加下面的消息定义到它的头文件,和一个BN_CLICKED(反射消息)的消息处理程序。
// 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 );
}标题窗体将发送一个包含其自己句柄的消息,如wParam,有了这个信息,它的父控件将可以了解到哪一个标题窗体已经被点击了。
现在,我们通过手工添加一个方法到其消息映射在CStackedWndCtrl中处理这个消息如下:
// In StackedWndCtrl.h
#define WM_RUBRIC_WND_CLICKED_ON ( WM_APP + 04100 )
...
...
// 生成消息映射函数
protected:
//{{AFX_MSG(CStackedWndCtrl)
afx_msg void OnSize(UINT nType, int cx, int cy);
/
来源:http://www.tulaoshi.com/n/20160219/1627027.html