返回首页


{S0}简介
在下面的文章中,我将描述一种机制,使得它可以使用锚对接,并为您的控件在对话框和窗口。
对接和控制锚是指部分或全部控制在一个对话框,或只是一个普通的窗口,根据父窗口(例如一个对话框或视图)的大小调整其大小和位置。你已经看到了这个机制,在许多应用中。在MS Outlook的接触形式是一个很好的例子。如果调整的形式,输入线,备忘录盒和其他控件也将缩小或增加自己的身高和/或宽度完全融入了新的窗口面积。
你也可以知道这一点。NET开发环境中的Windows窗体设计器的功能。如果你是一个性质NET开发和将要或从未使用过C或C编写Windows应用程序,那么这篇文章将帮助您十分。但对于那些仍然写ATL / MFC的应用程序或保持这种的,本文介绍的代码可能是一个很好的改善你的应用程序。背后的锚定的想法
停靠和锚定背后的想法很简单。你给用户一个可调整大小的窗口或对话框,在该窗口的元素调整其大小和位置,以保持他们的逻辑位置和大小,或调整它们的大小,以适合到父窗口的面积,让你没有浪费的空间或已消失的控制当窗口变成小。有了这个机制,用户可以调整窗口和控件的大小和经验,更灵活地与您的应用程序。
如果你写这个代码为每个对话和控制,如果你有很多,其中,你一定会需要一些时间完成这个任务。此外,它会成为一个枯燥的任务后,第二次或第三对话框。该BPCtrlAnchorMap
对接/锚定机制是解决初始化每个子窗口的初始大小和位置控制地图。当父窗口改变大小,一个处理函数被调用看起来控制图,并计算相对父窗口的大小变化的基础上控制新的大小和位置。
BPAnchorControlMap(这就是这篇文章是关于)提供这种控制映射机制和所有必需的功能,以及一些有用的宏。它提供了一种快速简便的方法,实施锚/停靠在你的对话框,您的代码编写工作,并减少到最低限度。所有你所要做的的是调用了两个函数,并宣布所谓的锚地图。实施的BPCtrlAnchorMap
BPCtrlAnchorMap可以实施在任何MFC或基于ATL的应用程序。一些小的修改,它也可以被用来在C或C纯粹的Win32应用程序,没有面向对象的框架。
下面的例子是基于MFC对话框的应用程序,并介绍了这些必要的步骤,使其工作。在同一类被声明为您或您的应用程序在一个共同的头文件一样stdafx.h中包括头文件中包含的文件bpctrlanchormap.h。请于您的类声明的宏DECLARE_ANCHOR_MAP()。这个宏产生名为InitAnchors和HandleAnchors我们两个必需的功能的声明。我们后来在我们的代码中调用这些函数。呼叫InitAnchors()函数内的OnInitDialog处理程序或你的窗口的OnCreate处理程序。重要的是,您的对话框或窗口的子窗口已经被创建,并有一个有效的窗口句柄分配给它,当你调用InitAnchors()。如果这一条件得到满足,你可以从你的代码在的任何地方调用InitAnchors(),但上述两个功能是最好的地方。 BOOL CAnchorMapDemoDlg::的OnInitDialog(){ CDialog的:的OnInitDialog(); / /一些初始化代码 / / ... / / ... InitAnchors(); 返回(TRUE);};
InitAnchors()函数可以被称为一个flags参数来指定额外的功能:ANIF_CALCSIZE(0x0001)
,如果您指定在调用到InitAnchors此标志,函数将计算本身所需的窗口大小。这是有用的对话框窗口大小到父窗口的面积适合InitAnchors呼叫前FormViews。 (见"使用表单视图锚地图")ANIF_SIZEGRIP(地图0x0002)
此标志会告诉初始化函数添加到窗口底部的右边缘的尺寸握。大小握会自动被隐藏的窗口最大化时,将显示窗口时是在"正常"或恢复状态。添加OnSize(WM_SIZE)到您的类的消息处理(claswizard会为你做这个)和呼叫HandleAnchors(AMP; rcWnd)。放大器; rcWnd的参数是一个RECT结构必须包含您的对话框或窗口的窗口坐标的实际的指针。你可以通过调用GetWindowRect(AMP; rcWnd)前这些坐标。无效CAnchorMapDemoDlg::OnSize(UINT nType,INT,INT CX CY){ CDialog的:OnSize(nType,CX,CY); CRECT rcWnd; GetWindowRect(AMP; rcWnd); HandleAnchors(AMP; rcWnd); / /你也可以传递NULL放大器; rcWnd };
您也可以致电HandleAnchors(空)让功能为您的坐标。在这种情况下,你的窗口类必须直接或间接(这是因为m_hWnd是需要)从CWnd派生。C或CPP文件内定义的锚地图。 (你必须这样做你的类的外)。锚地图定义看起来像一个消息映射的定义。在下一节,我们将讨论锚地图。看起来像这样一个简单的例子,一个锚地图。 BEIGN_ANCHOR_MAP(CMyDialog) ANCHOR_MAP_ENTRY(IDC_MYCONTROL,ANF_BOTTOM | ANF_RIGHT)END_ANCHOR_MAP()编译并运行您的应用程序定义锚地图
锚地图定义告诉BPCtrlAnchorMap.h代码在您的窗口控制应采取对接/锚定和如何这些控制行为的一部分。
你开始与宏观BEGIN_ANCHOR_MAP(theClass的)锚地图,你与你的窗口类的名称(例如CMyDialog)取代theClass的定义。
,在此之后,您可以定义一个或多个与宏观ANCHOR_MAP_ENTRY(nIDCtrl,nFlags)锚映射条目。该nIDCtrl参数是控制子窗口ID,应增加的对接/锚定机制。 nFlags的参数是一个或多个以下常量的组合,并指定控制的对接行为。
以下对接标志用于停靠控制一个窗口的一条边界。停靠控件将被移动到它停靠,将调整其宽度或高度相同的宽度或高度边境的窗口边界端,而其他尺寸(宽度或高度)的控制,将保持不变。注意ANF_DOCK标志不应该被结合在一起或与锚固标志相结合。ANF​​_DOCK_TOP(0x0001)
这标志码头的控制窗口的顶部。ANF​​_DOCK_BOTTOM(地图0x0002)
这标志码头的窗口底部的控制ANF​​_DOCK_LEFT(0x0004)
这标志码头的窗口左侧的控制ANF​​_DOCK_RIGHT(0x0008)
此标志将停靠右侧窗口的控制ANF​​_DOCK_ALL(0x000F)
此标志将停靠窗口的所有边境双方的控制ANF​​_DOCK_TOP_EX(0x0200)
码头窗口顶部的控制,但保持其原始的宽度和高度。ANF​​_DOCK_BOTTOM_EX(0x0400)
码头窗口底部的控制,但保持其原始和高度。ANF​​_DOCK_LEFT_EX(为0x0800)
码头的窗口左侧的控制,但保持itsnbsp;原始的宽度和高度ANF​​_DOCK_RIGHT_EX(0x1000)
码头窗口右侧的控制,但保持其原始的宽度和高度。
以下是锚定标志,它们被用来定义一个控制的边界,有一个固定的父窗口的边界,双方的距离。如果父窗口改变其大小,锚定的控制将沿着其边缘与父窗口的边缘。下列标志可以任意组合结合在一起。ANF​​_TOP(0x0010处)
控制父窗口顶部的距离将不断ANF​​_BOTTOM(0x0020)
控制父窗口底部的距离将不断ANF​​_LEFT(0x0040)
控制距离的父窗口的左侧将不断ANF​​_RIGHT(0x0080)
控制距离的父窗口的右侧将不断ANF​​_AUTOMATIC(0x0100)
这是一个特殊的标志,它可以让代码确定最佳的锚固法。你不应该与任何其他标​​志相结合这个标志。
此外,有一些特殊的标记,你可以结合对接/锚标志: ANF​​_ERASE(0x02000)
这是一个特殊的标志,它告诉EraseBackground()功能来擦除控制占用的面积。 EraseBackground()函数是一个特殊的功能,用于减少许多控件在Windows闪烁。欲了解更多有关此主题的的详细信息,请参阅"卸下闪烁"
特殊情况下为NULL
请注意,当你传递NULL或0控器- ID ANCHOR_MAP_ENTRY宏时,有一个特殊的情况。控制的ID为0的值,在父窗口的所有尚未添加到控制图的控制,将现在指定nFlags参数的增值效应。加快的方式来定义一个对话框锚地图添加条目ANCHOR_MAP_ENTRY(NULL,ANF_AUTOMATIC)。
这将添加所有控件自动确定每个控制的最佳锚固的方法。ANCHOR_MAP_ENTRY_RANGE宏
在最新版本中,我添加了可用于添加范围控制图的控制IDS ANCHOR_MAP_ENTRY_RANGE宏。这个宏用来作为ANCHOR_MAP_ENTRY完全相同的方式,除非你已经通过两个控制参数,指定范围内的第一个ID和一个最后的ID - ID的。第三个参数是正常的标志value.ANCHOR_MAP_ENTRY_RANGE(IDCB_SKILL1,IDCB_SKILL6,ANF_TOP | ANF_LEFT)
现在,您已经定义了所有的锚映射条目后,你最终调用宏END_ANCHOR_MAP()锚地图定义。
现在你可以测试和运行应用程序。使用表单视图锚地图
该BPControlAnchorMap机制最初是专为对话框对话框或父窗口和它的控制,都具有恒定的尺寸。出现一个问题,当您使用CFormView的机制。一个CFormView调整自身以适应到包含它的父窗口领域。由于这一过程发生之前,你可以调用InitAnchors(),控制图将是空的,控件将不能正确地调整大小。
一本的解决方法是使用标志ANIF_CALCSIZE,在调用函数InitAnchors时。如果你调用InitAnchors(ANIF_CALCSIZE),然后InitAnchors功能将试图找到原始大小的父窗口(您的CFormView),通过控制地图的所有控制的底部最右边的坐标。卸下忽隐忽现
在某些情况下,或者如果你有许多管制,控制和窗口可能会闪烁的大对话框时,调整大小。这是因为默认情况下实施的WM_ERASBKGND消息处理清除整个对话窗口和有关我们的控制。这个工程在标准对话框控件不移动或改变它们的大小。
摆脱闪烁降低到最低,我加入了一个特殊EraseBackground的功能,清除这是不是通过控制占用面积。该机制使用一个区域(HRGN)对象初始化窗口的客户端矩形。后该地区的对象已经初始化,EraseBackground功能循环所有的子窗口(不只是在控制图的),并从该地区的地区。最后,该地区是用背景色填充。这可以防止与背景颜色overpainted的子窗口。
时,我经历这种技术的一个问题是使用组盒。组盒不画自己内心的背景,因为整个地区组盒是从区域中删除,其背景是不填,thenbsp;组框变为"透明"。强制从该地区不被删除的组盒的面积,使用的ANF_ERASE标志,当您添加组框控件控制地图。
如果你想使用这个自定义的EraseBackground功能,添加一个WM_ERASEBKGND处理程序的窗口和调用m_bpfxAnchorMap.EraseBackground()而不是默认的implementation.BOOL CAnchorMapDemoDlg:OnEraseBkgnd(CDC * PDC){ / /这里我们称之为从EraseBackground处理程序 / /锚地图将减少闪烁。 返回(m_bpfxAnchorMap.EraseBackground(PDC - GT; m_hDC)); }幕后
下面的行会尽量给你见识到的所有宏。如果你感兴趣的代码是如何工作的,下载到bpctrlanchormap.h。
整个机制需要在一个特殊的类地方,当你调用DECLARE_ANCHOR_MAP将嵌入到您的对话框类。该文件bpctrlanchormap.h包括的这个类的代码(CBPCtrlAnchorMap)声明。宏观DECLARE_ANCHOR_MAP(),你在你的类声明中调用终于延伸到下面的C代码:CBPCtrlAnchorMap m_bpfxAnchorMap;无效InitAnchors(= 0布尔bFindCtrlEdges);无效HandleAnchors(RECT * pRect);
既然你声明你的类,成员m_bpfxAnchorMap和职能InitAnchors HandleAnchors内调用DECLARE_ANCHOR_MAP添加到您的类。
InitAnchors和HandleAnchors功能的实现(代码)生成与锚映射宏BEGIN_ANCHOR_MAP,ANCHOR_MAP_ENTRY和END_ANCHOR_MAP。
BEGIN_ANCHOR_MAP(theClass的)宏终于延伸到下面的代码:无效theClass的:HandleAnchors(RECT * pRect){ m_bpfxAnchorMap.HandleAnchors(pRect); } 无效theClass的:InitAnchors(布尔bFindCtrlEdges){
注意InitAnchors功能是没有用大括号封闭的。这是因为该函数的代码是人仍下落不明。它配备了ANCHOR_MAP_ENTRY(nCtrlID,nFlags)延伸到下面的代码:m_bpfxAnchorMap.AddControl(nIDCtrl,nFlags);
END_ANCHOR_MAP宏包含一些额外的代码为InitAnchors,最后输出的功能闭幕括号:m_bpfxAnchorMap.Initialize(m_hWnd,bFindCtrlEdges); RECT rcWnd; ::GetWindowRect(m_hWnd,放大器; rcWnd); &# 160;m_bpfxAnchorMap.HandleAnchors(AMP; rcWnd);};
现在,我们有我们准备使用的功能,我们可以调用我们的类InitAnchors HandleAnchors功能。剩下的就是做CBPCtrlAnchorMap代码。那么,多数民众赞成它。
我希望你会发现这个代码是有用的,也许有人可以帮助我减少闪烁时,与许多控制的大型对话框的大小。
再见,drice!

回答

评论会员:米赞拉赫曼 时间:2011/12/03


/米赞
评论会员:johnnyk4277 时间:2011/12/03
我只想说"自动让出锚的代码图"​​是神奇的,它正是我想要的第一次为我所有的控制。真棒
评论会员:bleubleu 时间:2011/12/03
,这是伟人
现在的调整将在MFC中容易。净!
另外的伟大工程的最新改进型的MFC,与VS2008的功能包。


评论会员:Mydraal 时间:2011/12/03
第一,这项工作非常好的。它的工作原理相当不错,很容易实现。

我做了一个增强的情侣,不过,我觉得将是有益的。

号码1。
当自动计算控制地位,在右下角的使用和控制是对框架的边缘卡住。不过,我觉得它会是一个不错的主意,看到多远控制从顶部和左缩进和使用右下角的值,以休会控制。

在"初始化",我添加了一个很少的代码来完成: / / 3的新线路 - 在此之前的最后两行插入
。 GetWindowRect(hWndParent,rcMaxBR); / /只抓一个值应该比任何控件左上角
rcMaxBR.left = rcMaxBR.right; / /初始化的东西比最低左
较大 rcMaxBR.top = rcMaxBR.bottom; / /初始化的东西比最低的顶部
rcMaxBR.right = 0; / /原代码
rcMaxBR.bottom = 0; / /""
然后在计算部分: / /开始于(现有)
(dwFlags中ANIF_CALCSIZE){
(iCrtl = 0; ICTRL  60; / /插入2号线
(m_Ctrl [ICTRL] rect.left (m_Ctrl [ICTRL] rect.top / / ...其余为循环
 60; }
/ /修改这两行包括rcMaxBR顶部和左侧
m_rcPrev.right = m_rcPrev.left m_rcMaxBR.left m_rcMaxBR.right;
m_rcPrev.bottom = m_rcPrev.top m_rcMaxBR.top m_rcMaxBR.bottom; 完成

2号:
Groupboxes不正确重绘。在您的代码中有一个选项,允许控制在OnEraseBkgnd忽略。我只是有点懒,我喜欢你的选项来设置控制自动。因此,在OnEraseBkgnd我添加了几行代码总是忽略组框。他们将闪烁一点点,因为他们重绘,但其余的不会。此外,反正你看它,GROUPBOXES可以不删除从后台过滤,因为他们将不痛的背景。

在OnEraseBkgnd: / / ...
bVisible =:IsWindowVisible(hWndChild)
 0; 如果(ICTRL!= -1 bVisible){
TCHAR szClassName [10]
::GetClassName(hWndChild,szClassName,10);

(_tcsicmp(_T("按钮"),szClassName)| |(BS_GROUPBOX =(GetWinddowLong(hWndChild,GWL_STYLE)BS_CHECKBOX))){
/ / ...所有的逻辑,从封闭的,如果
}
} 完成

正如我看,我看到更好的利用这个逻辑migh,在初始化相反,它可以设置ANF_ERASE标志。这将是更efficietn。

感谢您的代码。希望这些帮助别人。

Mydraal(DStephens)

评论会员:kalaveer 时间:2011/12/03
我有看到执行的exe它完美地工作在相同的东西在C可能#我想要做的同样的自动resizze但在我的形式大量的控制是有和另外我想控制
申请自动调整大小suchthat一些控制移动olny froms大小的增加距离的一半。如果可能的话,请Tellme公司。
评论会员:拉吉帕蒂尔 时间:2011/12/03

您好,

我已经创建了一个可停靠的窗口,并增加了2个编辑框和两个命令按钮it.This类是从CControlBar派生。我有编程方式添加窗口上的控制,并没有使用任何对话框。

添加锚消息映射后,我的控制消失。

请解释我有什么问题。



帕蒂尔:

| vbachmut
评论会员:游客 时间:2011/12/03
!相当bug的代码......仍然是一个不错的尝试,落实到应用程序的大小调整功能
vietdoorgroup
评论会员:游客 时间:2011/12/03
!大控制imgsrc=http://www.orcode.com/upimg/2011_12_03_21_22_56_1.gif--------------------------{BR}ERP的VN:{A}
亚历山大舍甫琴科
评论会员:为什么呢? 时间:2011/12/03
prcarp |删除闪烁,你需要做的的是WS_CLIPCHILDREN样式添加到您的对话框。可以做到用资源编辑器,或通过代码:

/ /删除闪烁::SetWindowLong函数(hParent,GWL_STYLE:GetWindowLong(hParent,GWL_STYLE)| WS_CLIPCHILDREN);
评论会员:drice 时间:2011/12/03
。闪烁问题的解决方案可以使用[开始|结束] DeferWindowPos(..)函数,而不是在循环中使用的唯一SetWindowPos(..)

修改如下文件BPCtrlAnchorMap.h:


@@ -467,4 +467,5 @@

       } else PreProcess(pRectParent); 

       

-> 	HDWP hWndPos = ::BeginDeferWindowPos(m_nCtrlCount);

       

       for (iCtrl=0; iCtrl < m_nCtrlCount; iCtrl++) {

 

@@ -585,5 +586,5 @@

           szCtrl.cx = pCtrl->rect.right-pCtrl->rect.left;

           szCtrl.cy = pCtrl->rect.bottom-pCtrl->rect.top;

<-         SetWindowPos(pCtrl->hWnd, NULL, (int)pCtrl->rect.left, (int)pCtrl->rect.top, (int)szCtrl.cx, (int)szCtrl.cy, SWP_NOZORDER | SWP_NOACTIVATE);

-> 		::DeferWindowPos(hWndPos, pCtrl->hWnd, NULL, (int)pCtrl->rect.left, (int)pCtrl->rect.top, (int)szCtrl.cx, (int)szCtrl.cy, SWP_NOZORDER | SWP_NOACTIVATE );

   

         };  

 

@@ -591,4 +592,5 @@

       };

   

-> 	::EndDeferWindowPos(hWndPos);

        PostProcess();


评论会员:drice 时间:2011/12/03
我也注意到,当控件的大小,内容不刷新,我看到以前的立场的产物(例如在头静态控制)

我把一个固定:InvalidateRect(pCtrl> HWND,NULL,FALSE)后,您DeferWindowPos,虽然它解决的问题,它重新引入闪烁

有另一种方式?

(BTW,我转换VC + + 6.0项目)
评论会员:drice 时间:2011/12/03
好了,我现在已经添加一个自定义的EraseBackground功能不overpaint的控制(见文章)。你调用HandleAnchors(在您的WM_SIZE消息处理程序)后,添加一个调用无效(假)。这将更新控制,也很容易和快速实施。



铜,drice又名。 A. Thiede