制作停靠窗体、悬浮窗Dockpanel
此功能在借鉴以下链接博文后验证实现,特此感谢,并做补充转发分享!
http://blog.csdn.net/why_dx/article/details/8751976
http://blog.csdn.net/jun502525164/article/details/9079481
http://blog.sina.com.cn/s/blog_9136935e0102vr38.html
http://blog.csdn.net/zzzhaohaizi/article/details/7064823
-------------------------------------------------------------- 分割线 ----------------------------------------------------
利用DockPanel与C#制作窗体浮动和停靠
点击功能窗 然后鼠标拖动form2的效果图如下:
解压文件得到如下图文件:
2、构建主窗体(父窗体):frmMain的。
(1)新建工程:FloatingForm
(2)将DockPanel.config和WeifenLuo.WinFormsUI.Docking.dll复制到当前项目的
FloatingForm\FloatingForm\bin\Debug文件下。
(3)首先添加引用WeifenLuo.WinFormsUI.Docking。
然后点击工具箱右键添加DockPanel控件到工具箱中。
(4)添加主窗体frmMain中,并设置主窗体的IsMdiContainer =true;
(5)在主窗体中添加的DockPanel控件:DockPanel1,并设置DockPanel中的documentstyle:
dockPanel.DocumentStyle = DocumentStyle.DockingMdi;
frmMain的界面如下:(如果添加dockpanel控件到frmMain出现错误则转到注意事项查看)
后台代码如下:
3. 构建需要浮动显示的窗体:FrmFunction。
(1)在当前工程中添加窗体:FrmFunction;(注意:浮动窗体和标签窗体需要继承自DockContent);
(2)为了保证在关闭某一浮动窗体之后,再打开时能够在原位置显示,要对浮动窗体处理,处理窗体的 DockstateChanged事件,标签窗体dock位置改变,记录到公共类;
frmFunction界面如下:(所要浮动的窗体)
后台代码如下:
1 using System; 2 3 using System.Collections.Generic; 4 5 using System.ComponentModel; 6 7 using System.Data; 8 9 using System.Drawing; 10 11 using System.Linq; 12 13 using System.Text; 14 15 usingSystem.Windows.Forms; 16 17 usingWeifenLuo.WinFormsUI.Docking; 18 19 20 21 namespace FloatingForm 22 23 { 24 25 public partial class frmFunction : DockContent 26 27 { 28 29 private static frmFunctionInstance; 30 31 32 33 public frmFunction() 34 35 { 36 37 InitializeComponent(); 38 39 } 40 41 public static frmFunction GetInstance() 42 43 { 44 45 if (Instance == null) 46 47 { 48 49 Instance = new frmFunction(); 50 51 } 52 53 return Instance; 54 55 } 56 57 //为了保证在关闭某一浮动窗体之后,再打开时能够在原位置显示,要对浮动窗体处理,处理窗体的DockstateChanged事件,标签窗体dock位置改变,记录到公共类 58 59 private void FrmFunction_DockStateChanged(object sender, EventArgs e) 60 61 { 62 63 //关闭时(dockstate为unknown) 不把dockstate保存 64 65 if (Instance != null) 66 67 { 68 69 if (this.DockState ==DockState.Unknown || this.DockState == DockState.Hidden) 70 71 { 72 73 return; 74 75 } 76 77 AppConfig.ms_FrmFunction =this.DockState; 78 79 } 80 81 } 82 83 private void FrmFunction_FormClosing(object sender, FormClosingEventArgse) 84 85 { 86 87 Instance = null; // 否则下次打开时报错,提示“无法访问已释放对象” 88 89 } 90 91 92 93 } 94 95 }
4. 在当前工程中添加类AppConfig(公共类)。
代码如下:
1 using System; 2 3 using System.Collections.Generic; 4 5 using System.Linq; 6 7 using System.Text; 8 9 using WeifenLuo.WinFormsUI.Docking; 10 11 12 13 namespace FloatingForm 14 15 { 16 17 class AppConfig 18 19 { 20 21 public static DockState ms_FrmFunction =DockState.DockLeft; // 功能窗体,左端停靠 22 23 } 24 25 }
5.成功运行,实现基本功能(停靠在中间的效果图)
注意事项
问题(1)描述:
vs2010添加WeifenLuo.WinFormsUI.Docking.DockPanel.dll文件后,从工具栏中添加DockPanel控件时报错,提示【类型 Universe 无法解析程序集: System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a。】
解决方法:
打开【项目】的FloatingForm属性,选择【应用程序】,,修改【目标Framework(所有配置)】选项,在下拉框选项中选择【.net Framework 4】即可。
dockpanel中提供了几个可用的类, 重要的有两个, 一是DockPanel, 一是DockContent, DockPanel是从panel继承出来的, 用于提供可浮动的dock的子窗口进行浮动和dock的场所, DockContent是从form类中继承出来的, 用于提供可浮动的窗口基类. 就是说: DockContent对象可以在DockPanel对象中任意贴边, 浮动, TAB化等. WeiFenLuo.winFormsUI.Docking.dll的使用 1.建立一个WinForm工程,默认生成了一个WinForm窗体Form1。 2.引用—>添加引用—>浏览—>weiFenLuo.winFormsUI.Docking.dll。 3.窗体属性IsMdiContainer设置为True。 4.工具箱—>右键—>选择项—>.net组件—>浏览—>weiFenLuo.winFormsUI.Docking.dll—>在工具箱出现dockPanel。 5.将dockPanel拖到窗体Form1上,设置Dock属性,我设置的是:Fill。 停靠窗体: 1.新建一个WinForm窗体Form2。 2.在代码中修改窗体继承于DockContent。 public partial class Form2 : DockContent { Form1 form1; private DockPanel dp; public Form2() { InitializeComponent(); } public Form2(Form1 fm1) { form1 = fm1; dp = (DockPanel)form1.Controls["dockPanel1"]; } } 3.在主窗体Form1中显示停靠窗体。 private void Form1_Load(object sender, EventArgs e) { Form2 form2 = new Form2(); form2.Show(this.dockPanel1, DockState.DockLeft); } dockpanel中其他几个类 DockWindow:用来划分dockpanel. 在一个DockPanel上面还有几个DockWindow把DockPanel分成了几块. 默认DockPanel用DockWindow创建了五个区域, 分别是DockTop, DockBottom, DockLeft, DockRight和Document, 任何一个DockPane都棣属于这五个区域中的某一个. DockPanel就是通过DockWindow来管理DockPane的所在位置的. DockPane: DockPanelSuit的一个基本显示单元, 最终用户看到的UI都是由DockPane组合而来的 FloatWindow: 事实上, FloatWindow跟DockPane是同等的, 只不过DockPane是附在DockWindow上, 而FloatWindow是一个浮动窗口而已. 显然, FloatWindow是一个Form, DockPanel管理着FloatWindow跟DockPane之间的转换, 而这个转换过程也无非就是把DockContent从FloatWindow转到DockPane上, 或者把DockContent从DockPane转到FloatWindow上, 然后显示出来 |
1.建立一个WinForm工程,默认生成了一个WinForm窗体Form1。
2.引用—>添加引用—>浏览—>weiFenLuo.winFormsUI.Docking.dll。
3.设置Form1窗体属性IsMdiContainer:True
4.工具箱—>右键—>选择项—>.net组件—>浏览—>weiFenLuo.winFormsUI.Docking.dll—>在工具箱出现dockPanel。
5.将dockPanel拖到窗体Form1上,设置Dock属性,我设置的是:Fill。
停靠窗体:
1.新建一个WinForm窗体Form2。
2.在代码中修改窗体继承于DockContent。
using WeifenLuo.WinFormsUI.Docking;
public partial class Form2 : DockContent
3.在主窗体Form1中显示停靠窗体。
private void Form1_Load(object sender, EventArgs e)
{
Form2 form2 = new Form2();
form2.Show(this.dockPanel1);
}
如果dockPanel嵌套在另1个容器控件上(如:panel),将dockPanel属性DocumentStyle设置为:DockingWindow/DockingSdi
DockPanel的基本使用 我就不说了,网上很多,我想说的是在使用DockPanel时 需要注意的几个小问题
第一个:
使用过DockPanel的人,都有可能会遇到这样一个错误:
Invalid Content: ActiveContent must be one of the visible contents, or null if there is no visible content.
翻译过来的意思大致是:无效的内容: 如果没有一个可见的内容,ActiveContent必须是可见的内容或空。
具体是什么原因,大家可以相互探讨下。下面我说说出现这个问题的几种情况
代码中的this关键字代表的就是Dockpanel所在的窗体为Form1
1)、当Dockpanel的DocumentStyle不为DockingMdi时,以下代码会出现这个问题
Frm_A frmA = null;
//判断子窗体中是否已经存在在DockPanel中
foreach (DockContent frm in this.dockPanel1.Contents)
{
if (frm is Frm_A)
{
frm.Activate(); //激活子窗体
return;
}
}
frmA = new Frm_A();
frmA.MdiParent = this;
frmA.Show(this.dockPanel1);
解决方案:看你设置Dockpanel的DocumnetStyle是否为DockingMdi。大家也可以试试其他几种方式(DockingWindow,DockingSdi,SystemMdi)
2)、设置了Dockpanel的DocumentStyle不为DockingMdi时,如果你想要设置窗体Frm_B为左边浮动窗体,需要设置窗体Frm_B的DockAreas为且仅为DockLeft,如果想要实现其他功能可自行去设置其他属性信息,现在请看下面代码
Frm_B frmB = null;
//判断子窗体中是否已经存在在DockPanel中
foreach (DockContent frm in this.dockPanel1.Contents)
{
if (frm is Frm_B)
{
frm.Activate(); //激活子窗体
return;
}
}
frmB = new Frm_B();
//frmB.MdiParent = this;
frmB.Show(this.dockPanel1,DockState.DockLeft);
注意,如果你在你的代码中加了红色注释的代码,那么程序运行时 也会报上面的那个错
解决方案:注释红色的代码。
原因:(个人理解)frmB.Show(this.dockPanel1,DockState.DockLeft);这句代码其实就设置了frmB只停靠在DockPanel左边,此时的frmB是不属于MDI子窗体的,所以一旦你加入红色的代码,程序就会报错。
第二个:
拖动、停靠、固定子窗体(显示在Dockpanel中)
拖动:如果你想使你的子窗体可以任意拖动,那么你在设置子窗体的DockAreas属性时,保持默认值,不要修改。
停靠:首先你需设置DockAreas的位置,可以停靠在左、右、下等,也可以通过程序代码控制,参考上面代码。
固定:只需设置你窗体的DockAreas为Document就行了
第三个:
子窗体和Contents的判断
很多时候你需要判断Dockpanel中存在多少个子窗体或Contents,请参考下面代码:
foreach(Form in this.MdiChildren)
{
//这样判断时,停靠的窗体是不会计算在内的
}
而
foreach (DockContent frm in this.dockPanel1.Contents)
{
//这样设置后,所有的继承与DockContent的窗体都会被计算在内的
}
第四个:
寻找主窗体、动态显示子窗体
实现的功能:这里我们需要实现,右键点击A窗体,通过右键菜单来显示窗体B。
1 //主窗体的对象 2 Form1 form1; 3 4 private void showB_Click(object senders, EventArgs e) 5 { 6 7 GetFrmMain(); //通过此函数来获取form1 8 9 foreach (Form frm in form1.MdiChildren) 10 { 11 if (frm is Frm_B) 12 { 13 frm.Activate(); 14 return; 15 } 16 } 17 18 Frm_B frmB = new Frm_B(this); 19 frmB.MdiParent = form1; 20 frmB.Show(form1.dockPanel1); 21 22 } 23 24 private void GetFrmMain() 25 26 { 27 28 if (this.Parent.Parent.Parent.Parent != null) 29 { 30 form1 = (Form1)this.Parent.Parent.Parent.Parent; 31 } 32 else 33 { 34 form1 = (Form1)this.Parent.Parent.Parent; 35 } 36 37 }
现在是在A窗体中,this关键字已经代码的不是主窗体了,那么这里我们就需要获取主窗体对象
当A窗体停靠时,需要this.Parent.Parent.Parent.Parent(四个)
不停靠时,只需要三个this.Parent.Parent.Parent
调试代码发现:停靠时
this.Parent 为 {WeifenLuo.WinFormsUI.Docking.DockPane}
this.Parent.Parent 为 {WeifenLuo.WinFormsUI.Docking.DockWindow, BorderStyle: System.Windows.Forms.BorderStyle.None}
this.Parent.Parent.Parent 为 {WeifenLuo.WinFormsUI.Docking.DockPanel, BorderStyle: System.Windows.Forms.BorderStyle.None}
this.Parent.Parent.Parent 为 {TestEvenhandler.Form1, Text: Form1} 就是我们要找的主窗体Form1
不停靠时:
this.Parent 为 {WeifenLuo.WinFormsUI.Docking.DockPane}
this.Parent.Parent 为 {WeifenLuo.WinFormsUI.Docking.DockPanel+AutoHideWindowControl, BorderStyle: System.Windows.Forms.BorderStyle.None}
this.Parent.Parent.Parent 为 {TestEvenhandler.Form1, Text: Form1} 就是我们要找的主窗体Form1
DockPanel使用方法
DockPanel有人曰浮动窗体,也就是c#编辑器的样式,如下图:
浮动窗体可以浮动、停靠(上下左右)、分页(如上图的档案录入页面)。
以下记录以下使用方法:
(1)首先找到WeifenLuo.WinFormsUI.Docking.dll,下载WeifenLuo.WinFormsUI.Docking.dll组建(点击下载)。
(2)把该组建添加到引用,创建窗体1为主窗体,窗体2、窗体3为子窗体。把dockpanle工具添加上。
(3)主窗体代码:把dockpanle拖放到主窗体,添加代码 dockPanel1.DocumentStyle = DocumentStyle.DockingMdi;
(4)子窗体:子窗体继承自: WeifenLuo.WinFormsUI.Docking.DockContent , 不是继承自form;定义子窗体对象
form1 f1=new form1();
f1.ShowHint = DockState.Document;
f1.Show(dockPanel1);
显示效果即得到。
另外也记录下状态,下次打开时候任然保持,用以下方法加载记录状态:
(1)private DeserializeDockContent ddc;
(2)private IDockContent GetContentFromPersistString(string persistString)
{
if (persistString == typeof(f1).ToString())
return f1;
if (persistString == typeof(f2).ToString())
return f2;
else
{
return null;
}
}
(3)
string configFile = Path.Combine(Path.GetDirectoryName(Application.ExecutablePath), "DockPanel.config");
if (File.Exists(configFile))
dockPanel1.LoadFromXml(configFile, ddc);
ddc = new DeserializeDockContent(GetContentFromPersistString);
以上方法加载保存的状态,
string configFile = Path.Combine(Path.GetDirectoryName(Application.ExecutablePath), "DockPanel.config");
dockPanel1.SaveAsXml(configFile);
另外,要实现各个dockpanle子窗体之间的互动,例如,vs编辑器中,设计界面的时候,选择一个文本框,属性框中的属性随之改变,使用委托可以解决,可以参看本博客的委托应用1.
布局控件"WeifenLuo.WinFormsUI.Docking"是一个非常棒的开源控件,用过的人都深有体会,该控件之强大、美观、不亚于商业控件。而且控件使用也是比较简单的。先看看控件使用的程序界面展示效果。
我在几个共享软件都使用了该布局控件,我们先以“深田之星送水管理系统网络版”这款软件为例,介绍如何完成该界面的设计及显示的。
1、首先,我们添加一个主界面窗体,命名为MainForm,该窗体IsMdiContainer设置为True,也就是设置为多文档窗体格式。拖拉布局控件"WeifenLuo.WinFormsUI.Docking.DockPanel"到主窗体MainForm中,并设置下面几个属性:
Dock为Fill、DocumentStyle为DockingMdi、RightToLeftLayout为True。
这几个属性的意思应该不难,Dock就是 覆盖整个MDI窗体的区域,DocumentStyle为多文档类型、RightToLeftLayout是指新打开的窗口都停靠在右边区域。
2、主界面其实基本上就可以了,另外我们看到“送水管理系统网络版”的界面中有一个左边的工具栏,它其实也是在一个停靠的窗体中的,我们增加一个窗体用来承载相关的工具快捷键按钮展示。命名为MainToolWindow的窗体,继承自WeifenLuo.WinFormsUI.Docking.DockContent.
其中的“HideOnClose”属性很重要,该属性一般设置为True,就是指你关闭窗口时,窗体只是隐藏而不是真的关闭。
左边的窗口MainToolWindow实现停靠的代码是在MainForm的构造函数或者Load函数中加载即可。
mainToolWin.Show(this.dockPanel, DockState.DockLeft);
3、对于工具窗口我们已经完成了,但是主业务窗口还没有做,也就是下面的部分内容。
为了方便,我们定义一个基类窗体,命名为BaseForm,继承自DockContent,如下所示
public class BaseForm : DockContent
然后每个业务窗口继承BaseForm即可。
4、剩下的内容就是如何在主窗体MainForm中展示相关的业务窗口了,展示的代码如下所示
1 public partial class MainForm : Form 2 { 3 #region 属性字段 4 5 private MainToolWindow mainToolWin = new MainToolWindow(); 6 private FrmProduct frmProduct = new FrmProduct(); 7 private FrmCustomer frmCustomer = new FrmCustomer(); 8 private FrmOrder frmOrder = new FrmOrder(); 9 private FrmStock frmStock = new FrmStock(); 10 private FrmComingCall frmComingCall = new FrmComingCall(); 11 private FrmDeliving frmDeliving = new FrmDeliving(); 12 private FrmTicketHistory frmHistory = new FrmTicketHistory(); 13 14 #endregion 15 16 public MainForm() 17 { 18 InitializeComponent(); 19 20 SplashScreen.Splasher.Status = "正在展示相关的内容dockPanel用法"; 21 System.Threading.Thread.Sleep(100); 22 23 mainToolWin.Show(this.dockPanel, DockState.DockLeft); 24 frmComingCall.Show(this.dockPanel); 25 frmDeliving.Show(this.dockPanel); 26 frmHistory.Show(this.dockPanel); 27 frmStock.Show(this.dockPanel); 28 frmProduct.Show(this.dockPanel); 29 frmCustomer.Show(this.dockPanel); 30 frmOrder.Show(this.dockPanel); 31 32 SplashScreen.Splasher.Status = "初始化完毕dockPanel用法"; 33 System.Threading.Thread.Sleep(50); 34 35 SplashScreen.Splasher.Close(); 36 } 37
private void menu_Window_CloseAll_Click(object sender, EventArgs e) { CloseAllDocuments(); } private void menu_Window_CloseOther_Click(object sender, EventArgs e) { if (dockPanel.DocumentStyle == DocumentStyle.SystemMdi) { Form activeMdi = ActiveMdiChild; foreach (Form form in MdiChildren) { if (form != activeMdi) { form.Close(); } } } else { foreach (IDockContent document in dockPanel.DocumentsToArray()) { if (!document.DockHandler.IsActivated) { document.DockHandler.Close(); } } } } private DockContent FindDocument(string text) { if (dockPanel.DocumentStyle == DocumentStyle.SystemMdi) { foreach (Form form in MdiChildren) { if (form.Text == text) { return form as DockContent; } } return null; } else { foreach (DockContent content in dockPanel.Documents) { if (content.DockHandler.TabText == text) { return content; } } return null; } } public DockContent ShowContent(string caption, Type formType) { DockContent frm = FindDocument(caption); if (frm == null) { frm = ChildWinManagement.LoadMdiForm(Portal.gc.MainDialog, formType) as DockContent; } frm.Show(this.dockPanel); frm.BringToFront(); return frm; } public void CloseAllDocuments() { if (dockPanel.DocumentStyle == DocumentStyle.SystemMdi) { foreach (Form form in MdiChildren) { form.Close(); } } else { IDockContent[] documents = dockPanel.DocumentsToArray(); foreach (IDockContent content in documents) { content.DockHandler.Close(); } } }