MFC -CDockablePane 停靠窗口学习
这里写目录标题
创建CDockablePane停靠窗口
步骤
- 定义一个继承自CDockablePane的类
// h文件
#pragma once
#include "afxdockablepane.h"
class CObjectWnd :public CDockablePane
{
public:CObjectWnd(void);~CObjectWnd(void);DECLARE_MESSAGE_MAP()afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);afx_msg void OnSize(UINT nType, int cx, int cy);};// cpp文件
#include "stdafx.h"
#include "ObjectWnd.h"CObjectWnd::CObjectWnd(void)
{
}CObjectWnd::~CObjectWnd(void)
{
}BEGIN_MESSAGE_MAP(CObjectWnd, CDockablePane)ON_WM_CREATE()ON_WM_SIZE()
END_MESSAGE_MAP()int CObjectWnd::OnCreate(LPCREATESTRUCT lpCreateStruct)
{if (CDockablePane::OnCreate(lpCreateStruct) == -1)return -1;return 0;
}void CObjectWnd::OnSize(UINT nType, int cx, int cy)
{CDockablePane::OnSize(nType, cx, cy);
}
- MainFrm.h中声明
MainFrm.h中添加头文件ObjectWnd.h,并定义CObjectWnd类的对象
include "ObjectWnd.h"...CObjectWnd m_wndObject;
- MainFrm.cpp中添加响应程序
BOOL CMainFrame::CreateDockingWindows() 函数中添加:
CString strObjectView("面向对象");if (!m_wndObject.Create(strObjectView, this, CRect(0, 0, 200, 200), TRUE, 1001, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | CBRS_LEFT | CBRS_FLOAT_MULTI)){TRACE0("未能创建“面向对象”窗口\\n");return FALSE; // 未能创建}
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) 函数中添加:
m_wndObject.EnableDocking(CBRS_ALIGN_ANY); //使可停靠与浮动DockPane(&m_wndObject);
创建完成,运行即可显示。
CDockablePane停靠窗口的残影问题
停靠窗口隐藏后,鼠标移动到停靠窗口标签上显示停靠窗口,会有残影。
注意:残影问题未解决!!!!下述方法测试失败,原因正在查找中
解决方法:
① 在CObjectWnd 类中添加OnCreate和OnSize函数
② CObjectWnd::OnCreate中创建控件
CRect rectDummy;rectDummy.SetRectEmpty();const DWORD dwViewStyle = WS_CHILD|WS_VISIBLE|TVS_HASLINES|TVS_HASBUTTONS|WS_CLIPSIBLINGS|WS_CLIPCHILDREN;if(!m_pointsInfo.Create(dwViewStyle,rectDummy,this,/*ID*/)){TRACE0(MyLoadString(IDS_CREATEPOINTINFOVIEW_ERROR));return -;}
③CDockable::OnSize中根据停靠窗口位置调整控件大小
if(GetSafeHwnd() == NULL){return;}CRect rectClient;GetClientRect(rectClient);m_pointsInfo.SetWindowPos(NULL,rectClient.left+,rectClient.top+,rectClient.Width()-,rectClient.Height()-,SWP_NOACTIVATE|SWP_NOZORDER);
Q:隐藏停靠窗口右键菜单
A:添加WM_CONTEXTMENU消息,不实现其内容即可
afx_msg void OnContextMenu(CWnd* pWnd,CPoint point);
停靠窗口中添加控件
本文主要介绍:在MFC创建的停靠窗口中添加一些控件,浮动窗口中可以添加MFC自身的控件,也可以添加对话框。
一、创建对话框
对话框属性做以下修改:
二、窗口中添加控件
ObjectWnd.h文件:
#pragma once
#include "afxdockablepane.h"
#include "ObjectDlg.h"
#include "afxwin.h"class CObjectWnd :public CDockablePane
{
public:CObjectWnd(void);~CObjectWnd(void);DECLARE_MESSAGE_MAP()afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); // 必须有 afx_msg void OnSize(UINT nType, int cx, int cy); // 在此填写改变尺寸后的重绘函数afx_msg void OnDestroy();void AdjustLayout();// 定义三个控件CEdit m_edit;CStatic m_static;CObjectDlg m_objectDlg; // 自定义的对话框类,停靠窗口内显示的对话框界面
};
ObjectWnd.cpp文件:
#include "stdafx.h"
#include "ObjectWnd.h"
#include "resource.h"CObjectWnd::CObjectWnd(void)
{
}CObjectWnd::~CObjectWnd(void)
{
}BEGIN_MESSAGE_MAP(CObjectWnd, CDockablePane)ON_WM_CREATE()ON_WM_SIZE()ON_WM_DESTROY()
END_MESSAGE_MAP()void CObjectWnd::AdjustLayout()
{if (GetSafeHwnd () == NULL || (AfxGetMainWnd() != NULL && AfxGetMainWnd()->IsIconic())) // IsIconic()作用是判断窗口是否处于最小化状态(点击了最小化按钮之后)。{return;}CRect rectClient;GetClientRect(rectClient); // 获得客户窗口尺寸矩形int height = rectClient.Height()/3;//控件在窗口中所占空间大小 动态调整控件的位置及尺寸m_edit.SetWindowPos(this,rectClient.left,rectClient.top,rectClient.Width(),rectClient.Height()/3,SWP_NOACTIVATE | SWP_NOZORDER); m_static.SetWindowPos(this,rectClient.left,rectClient.top+height,rectClient.Width(),rectClient.Height()/3,SWP_NOACTIVATE | SWP_NOZORDER);m_objectDlg.SetWindowPos(this,rectClient.left,rectClient.top+height*2,rectClient.Width(),rectClient.Height()/3,SWP_NOACTIVATE | SWP_NOZORDER);/*
SetWindowPos函数详解:
SetWindowPos(this->Handle, HWND_TOPMOST, 0, 0, 0,0,SWP_NOMOVE|SWP_NOSIZE|SWP_SHOWWINDOW|SWP_NOACTIVATE );//声明:
SetWindowPos(
hWnd: HWND; {窗口句柄}
hWndInsertAfter: HWND; {窗口的 Z 顺序}
X, Y: Integer; {位置}
cx, cy: Integer; {大小}
uFlags: UINT {选项}
): BOOL;
//hWndInsertAfter 参数可选值:
HWND_TOP = 0; {在前面}
HWND_BOTTOM = 1; {在后面}
HWND_TOPMOST = HWND(-1); {在前面, 位于任何顶部窗口的前面}
HWND_NOTOPMOST = HWND(-2); {在前面, 位于其他顶部窗口的后面}
//uFlags 参数可选值:
SWP_NOSIZE = 1; {忽略 cx、cy, 保持大小}
SWP_NOMOVE = 2; {忽略 X、Y, 不改变位置}
SWP_NOZORDER = 4; {忽略 hWndInsertAfter, 保持 Z 顺序}
SWP_NOREDRAW = 8; {不重绘}
SWP_NOACTIVATE = $10; {不激活} 不激活窗口,如果未设置标志,则窗口被激活,会被设置到其他最高级窗口或非最高级组的顶部;(设置这个属性,子窗口就不会再设置位置的时候,造成父窗口非最顶层窗口,出现闪屏)
SWP_FRAMECHANGED = $20; {强制发送 WM_NCCALCSIZE 消息, 一般只是在改变大小时才发送此消息}
SWP_SHOWWINDOW = $40; {显示窗口}
SWP_HIDEWINDOW = $80; {隐藏窗口}
SWP_NOCOPYBITS = $100; {丢弃客户区}
SWP_NOOWNERZORDER = $200; {忽略 hWndInsertAfter, 不改变 Z 序列的所有者}
SWP_NOSENDCHANGING = $400; {不发出 WM_WINDOWPOSCHANGING 消息}
SWP_DRAWFRAME = SWP_FRAMECHANGED; {画边框}
SWP_NOREPOSITION = SWP_NOOWNERZORDER;{}
SWP_DEFERERASE = $2000; {防止产生 WM_SYNCPAINT 消息}
SWP_ASYNCWINDOWPOS = $4000; {若调用进程不拥有窗口, 系统会向拥有窗口的线程发出需求}*/}int CObjectWnd::OnCreate(LPCREATESTRUCT lpCreateStruct)
{if (CDockablePane::OnCreate(lpCreateStruct) == -1)return -1;CRect rectDummy;rectDummy.SetRectEmpty(); // 创建组合:const DWORD dwViewStyle = WS_CHILD | WS_VISIBLE | CBS_DROPDOWNLIST | WS_BORDER | CBS_SORT | WS_CLIPSIBLINGS | WS_CLIPCHILDREN;// 动态创建控件 控件尺寸为0if (!m_edit.Create(dwViewStyle, rectDummy, this, 1)){TRACE0("未能创建CEdit控件 \\n");return -1; // 未能创建}// 动态创建控件if(!m_static.Create(NULL,dwViewStyle,rectDummy,this,6)){TRACE0("未能创建CStatic控件\\n");return -1; // 未能创建}// 创建对话框窗口:if (!m_objectDlg.Create(IDD_ObjectDlg,this)) // 创建非摸对话框{TRACE0("未能创建对话框窗口\\n");return -1; // 未能创建}m_objectDlg.ShowWindow(SW_SHOW); // 显示对话框AdjustLayout();//m_edit.SetWindowText(_T("CEdit控件"));//m_static.SetWindowText(_T("CStatic控件"));return 0;
}void CObjectWnd::OnSize(UINT nType, int cx, int cy)
{CDockablePane::OnSize(nType, cx, cy);AdjustLayout(); // 自动调整控件尺寸
}void CObjectWnd::OnDestroy()
{CDockablePane::OnDestroy();m_objectDlg.DestroyWindow();// TODO: 在此处添加消息处理程序代码
}
三、添加完成,运行如下:
MFC的停靠窗口中插入对话框,在对话框中添加控件并做控件自适应
单文档程序添加了停靠窗口后,可能会在停靠窗口中添加一些控件。在这里我的做法是在对话框上添加控件并布局,然后将这个对话框插入到停靠窗口中。
步骤
-
插入对话框,在对话框中放入控件(我的为树形控件),并新建对话框类CTestDlg
-
在停靠窗口类OnCreate函数中,插入对话框
m_testDlg.Create(对话框ID,this);
m_testDlg.ShowWindow(SW_SHOW);
- 在停靠窗口类的OnSize函数中调整对话框的位置
if(GetSafeHwnd() == NULL){return;}
CRect rectClient;
GetClientRect(rectClient);
m_trdConfigDlg.SetWindowPos(NULL,rectClient.left+1,rectClient.top+1,rectClient.Width()-2,rectClient.Height()-2,SWP_NOACTIVATE|SWP_NOZORDER);
- 在对话框中调整控件的大小
if(m_trdPageTree.GetSafeHwnd() == NULL){return;}
m_trdPageTree.MoveWindow(1,1,cx-2,cy-2);
停靠窗口添加工具条
先创建工具条,设ID为IDR_Object,修改程序如下:
ObjectWnd.h修改:
#pragma once
#include "stdafx.h"
#include "afxdockablepane.h"
#include "ObjectDlg.h"
#include "afxwin.h"//添加继承类
class CObjectToolBar : public CMFCToolBar
{
public:virtual void OnUpdateCmdUI(CFrameWnd* /*pTarget*/, BOOL bDisableIfNoHndler){CMFCToolBar::OnUpdateCmdUI((CFrameWnd*) GetOwner(), bDisableIfNoHndler);}virtual BOOL AllowShowOnList() const { return FALSE; }
};class CObjectWnd :public CDockablePane
{
public:CObjectWnd(void);~CObjectWnd(void);DECLARE_MESSAGE_MAP()afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);afx_msg void OnSize(UINT nType, int cx, int cy);afx_msg void OnDestroy();CObjectToolBar m_wndToolBar;//工具条对象CObjectDlg m_objectDlg; //对话框类void AdjustLayout();};
ObjectWnd.cpp修改:
#include "stdafx.h"
#include "ObjectWnd.h"
#include "resource.h"
#include "MainFrm.h"
#include "CSDNtest.h"//添加此头文件(工程名.h)CObjectWnd::CObjectWnd(void)
{
}CObjectWnd::~CObjectWnd(void)
{
}BEGIN_MESSAGE_MAP(CObjectWnd, CDockablePane)ON_WM_CREATE()ON_WM_SIZE()ON_WM_DESTROY()
END_MESSAGE_MAP()void CObjectWnd::AdjustLayout()
{if (GetSafeHwnd () == NULL || (AfxGetMainWnd() != NULL && AfxGetMainWnd()->IsIconic())){return;}CRect rectClient;GetClientRect(rectClient);//设置工具条位置int cyTlb = m_wndToolBar.CalcFixedLayout(FALSE, TRUE).cy;m_wndToolBar.SetWindowPos(NULL, rectClient.left, rectClient.top, rectClient.Width(),cyTlb, SWP_NOACTIVATE | SWP_NOZORDER);//控件在窗口中所占空间大小m_objectDlg.SetWindowPos(this,rectClient.left,rectClient.top+cyTlb,rectClient.Width(),rectClient.Height()-cyTlb,SWP_NOACTIVATE | SWP_NOZORDER);}int CObjectWnd::OnCreate(LPCREATESTRUCT lpCreateStruct)
{if (CDockablePane::OnCreate(lpCreateStruct) == -1)return -1;CRect rectDummy;rectDummy.SetRectEmpty();// 创建组合:const DWORD dwViewStyle = WS_CHILD | WS_VISIBLE | CBS_DROPDOWNLIST | WS_BORDER | CBS_SORT | WS_CLIPSIBLINGS | WS_CLIPCHILDREN;//创建工具条m_wndToolBar.Create(this, AFX_DEFAULT_TOOLBAR_STYLE, IDR_Object);m_wndToolBar.LoadToolBar(IDR_Object, 0, 0, TRUE /* 已锁定*/);m_wndToolBar.CleanUpLockedImages();m_wndToolBar.LoadBitmap(theApp.m_bHiColorIcons ? IDR_Object : IDR_Object, 0, 0, TRUE /* 锁定*/);m_wndToolBar.SetPaneStyle(m_wndToolBar.GetPaneStyle() | CBRS_TOOLTIPS | CBRS_FLYBY);m_wndToolBar.SetPaneStyle(m_wndToolBar.GetPaneStyle() & ~(CBRS_GRIPPER | CBRS_SIZE_DYNAMIC | CBRS_BORDER_TOP | CBRS_BORDER_BOTTOM | CBRS_BORDER_LEFT | CBRS_BORDER_RIGHT));m_wndToolBar.SetOwner(this);// 所有命令将通过此控件路由,而不是通过主框架路由:m_wndToolBar.SetRouteCommandsViaFrame(FALSE);// 创建对话框窗口:if (!m_objectDlg.Create(IDD_ObjectDlg,this)){TRACE0("未能创建对话框窗口\\n");return -1; // 未能创建}m_objectDlg.ShowWindow(SW_SHOW);AdjustLayout();return 0;
}void CObjectWnd::OnSize(UINT nType, int cx, int cy)
{CDockablePane::OnSize(nType, cx, cy);AdjustLayout();
}void CObjectWnd::OnDestroy()
{CDockablePane::OnDestroy();m_objectDlg.DestroyWindow();// TODO: 在此处添加消息处理程序代码
}
运行结果:
MFC系统自动生成的停靠窗格关掉后,如何重新显示?
EnableLoadDockState() 记录停靠窗状态 使能
停靠窗格关闭后,再也显示不出来了?原来,系统会默认记忆上次的状态,可以用函数来清除这个设置:
在MainFrame那个类中,CreateDocablePane之前,调用EnableLoadDockState(FALSE);即可。
EnableLoadDockState(FALSE); // 记录停靠窗状态 使能, 注意该修改会导致ribbon和dockable都状态都不记录
m_wndRibbonBar.Create(this);
m_wndRibbonBar.LoadFromResource(IDR_RIBBON);
去掉停靠窗口右上角关闭按钮
m_ourPane.SetControlBarStyle(~AFX_CBRS_CLOSE)
禁止用户拖动可停靠窗口
m_pane.SetControlBarStyle(AFX_CBRS_RESIZE);
代码实现隐藏的窗格的再次显示
如果MFC程序不是Ribbon风格的,可以通过在View视图菜单里添加如下代码实现隐藏的窗格的再次显示:
CDockablePane::ShowPane();