在WinForm项目中要求实现动态加载DockPanel。
简单研究了下,演示代码如下:
1 DockPanel runPanel = dockManager1.AddPanel(DockingStyle.Left); 2 runPanel.Name = “Dock1”; 3 4 DockPanel samePanel = null; 5 //查找相同位置是否已有停靠插件 6 foreach (DockPanel hasPanel in dockManager1.Panels) 7 { 8 if (hasPanel.Name != "Dock1" && hasPanel.Dock = = DockingStyle.Left) 9 { 10 samePanel = hasPanel; 11 break; 12 } 13 } 14 15 if (samePanel != null) 16 { 17 runPanel.DockAsTab(samePanel); 18 } 19 20 runPanel.show();
很简单几行代码,实现了基本意图。看起来问题很快解决。
但是实际应用中发现几个问题:
1、当第一次运行时,dockManager1.AddPanel后dockManager1.Panels.Count值为1;当DockAsTab后,dockManager1.Panels.Count值为3,系统居然自动增加了一个DockPanel。
2、使用DockAsTab方式,新的DockPanel总是在已有DockPanel右边生成,然后再融合进去,用户体验非常差,尤其是主程序中间有Mdi子程序时,总会有屏幕不停闪烁的效果。
这是怎么回事呢?
通过研究DockPanel的相关文档,终于明白DockPanel的运行机制。
在论述下面的一些观点前,先说明笔者使用的是DX11版本带的DockManager和DockPanel控件。
一个典型的DockPanel如下图。它包括DockPanel主体和容器两个部分。DockPanel中要承载其他控件时,控件容器必须建立。
在DX中,实例化一个DockPanel有三个方法。第一种方法示例如下:
DockPanel runPanel = new DockPanel();
该方法有两点需要注意:
Ø 它不会自动建立控件容器,需要使用者手工添加,代码示例如下:
ControlContainer _dockPanelContainer =newDevExpress.XtraBars.Docking.ControlContainer()
_dockPanelContainer.Name= "dockPanel_Container";
runPanel.Controls.Add(_dockPanelContainer);
//添加控件
runPanel.ControlContainer.Add(newControl());
Ø 它需要手工注册到DockManager中去
runPanel.Register(dockManager1);
第二种方法是常用的方法,示例如下:
DockPanel runPanel = dockManager1.AddPanel(DockStyle.Left);
它会自动注册DockPanel到DockManager,同时添加一个ControlContainer。
第三种方法示例如下:
DockPanel runPanel = sameDockPanel.AddPanel();
它由一个已存在的DockPanel创建一个新的DockPanel。
该新DockPanel的ParentPanel不是创建者,而是由系统自己建立的一个PanelContainer,该PanelContainer同时被设置为RootPanel。
停靠模式
DockPanel有两种停靠模式,Split和Tab模式。
Split模式界面示例如下图,两个DockPanel根据实际停靠风格并立在一起。
默认情况下,当我们建立好两个DockPanel,将它们直接show出来,它们呈现的就是如下图的Split模式。
Tab模式如下图,两个DockPanel是以分页样式融合在一起。示例代码如下:
using DevExpress.XtraBars.Docking; // ... //创建一个左停靠控件 DockPanel panel1 = dockManager1.AddPanel(DockingStyle.Left); panel1.Text = "runDockPanel1"; //由panel1添加一个停靠控件; //如果直接把panel1和panel2都show出来,我们可以看到它们是Split模式 DockPanel panel2 = panel1.AddPanel(); panel2.Text = " runDockPanel2"; //将二者的共同父容器设置为Tab模式 DockPanel container = panel1.ParentPanel; container.Tabbed = true;
最后演示效果图如下:
在这里需要明确的是:
当某个位置(如左边)只有一个DockPanel时,该DockPanel的ParentPanel和RootPanel都是自己。当存在两个及以上DockPanel时,所有DockPanel的ParentPanel和RootPanel都是由系统生成的一个公共DockPanelContainer作为父容器,该容器容纳所有同位置的DockPanel。
在关闭同位置的DockPanel时,若只剩下一个DockPanel,则DockPanelContainer会被系统自动释放。
明白了上面的这些原理,对解决上面提出的两个问题就找到了答案。
第一个问题如上所述,当同一位置存在两个或两个以上的DockPanel时,系统自动生成一个容器来容纳。
第二个问题,当在使用DockAsTab前,两个DockPanel是Split模式,之后才变为Tab模式。解决方法就是使用已存在的DockPanel建立新的DockPanel,然后把二者的ParentPanel设置为Tab模式。
另外在这里提一下,DockPanel中默认只能放置UserControl,如果使用Form类型,需要把TopLevel设置下,但是放置在DockPanel中的Form太有个性了。
一个典型的Tab模式DockPanel如上图。
最后,贴出部分实际代码供大家参考:
1 /// <summary> 2 /// 建立停靠控件t 3 /// </summary> 4 /// <param name="sCaption">标题</param> 5 /// <param name="sName">名称</param> 6 /// <param name="ctl">控件实例</param> 7 /// <param name="dock">停靠位置?</param> 8 /// <param name="dockManager">停靠管理器</param> 9 /// <param name="IsNew">是否多次新建窗口</param> 10 /// <returns>停靠对象</returns> 11 [DisplayName("建立停靠控件")] 12 public static DockPanel CreateCtl(string sCaption, string sName, Control ctl, enum_DockLocation dock, 13 DockManager dockManager, bool IsNew = false) 14 { 15 try 16 { 17 DockManager runDockManager = dockManager; 18 DockPanel newDockPanel = null; 19 //查找当前列表中是否已有同名的dockpanel 20 DockPanel oldPaneled = null; 21 22 runDockManager.BeginUpdate(); 23 24 //如果是多次新建窗口,则不查找已有Dock 25 if (!IsNew) 26 { 27 oldPaneled = GetHaveDockPanel(runDockManager, sName); 28 } 29 30 31 if (oldPaneled == null) 32 { 33 DockingStyle dockStyle = DockingStyle.Float; 34 switch (dock) 35 { 36 case enum_DockLocation.Left: 37 dockStyle = DockingStyle.Left; 38 break; 39 case enum_DockLocation.Buttom: 40 dockStyle = DockingStyle.Bottom; 41 break; 42 case enum_DockLocation.Right: 43 dockStyle = DockingStyle.Right; 44 break; 45 case enum_DockLocation.Center: 46 dockStyle = DockingStyle.Fill; 47 break; 48 default: 49 break; 50 } 51 52 DockPanel samePaneled = GetSameDockPanel(runDockManager, dockStyle, sName); 53 if (samePaneled != null) 54 { 55 newDockPanel = samePaneled.AddPanel(); 56 newDockPanel.Dock = DockingStyle.Fill; 57 } 58 else 59 { 60 newDockPanel = runDockManager.AddPanel(dockStyle); 61 } 62 63 if (newDockPanel != null) 64 { 65 newDockPanel.ID = new System.Guid(); 66 newDockPanel.Name = sName; 67 newDockPanel.Text = sCaption; 68 //newDockPanel.Options.ShowAutoHideButton = false; 69 70 if (ctl is Form) 71 { 72 Form srcForm = ctl as Form; 73 ctl = ControlLoader.CopyForm2Control(srcForm); 74 } 75 ctl.Dock = DockStyle.Fill; 76 ctl.Visible = true; 77 newDockPanel.Width = ctl.Width; 78 newDockPanel.Height = ctl.Height; 79 newDockPanel.Controls.Add(ctl); 80 81 //浮动停靠窗体处理 82 if (newDockPanel.Dock.Equals(DockingStyle.Float)) 83 { 84 newDockPanel.FloatForm.Size = ctl.Size; 85 newDockPanel.FloatForm.StartPosition = FormStartPosition.CenterScreen; 86 newDockPanel.FloatForm.AutoSizeMode = AutoSizeMode.GrowOnly; 87 newDockPanel.FloatForm.AutoSize = true; 88 } 89 90 newDockPanel.ClosingPanel += new DockPanelCancelEventHandler(ClosingPanel); 91 newDockPanel.ClosedPanel += new DockPanelEventHandler(ClosedPanel); 92 } 93 } 94 else 95 { 96 newDockPanel = samePaneled; 97 } 98 //在DockManager中注册当前插件 99 newDockPanel.Register(runDockManager); 100 101 if (newDockPanel.ParentPanel != null) 102 { 103 newDockPanel.ParentPanel.Tabbed = true; 104 } 105 106 newDockPanel.Visibility = DockVisibility.Visible; 107 newDockPanel.Visible = true; 108 109 runDockManager.EndUpdate(); 110 111 return newDockPanel; 112 } 113 catch (Exception ex) 114 { 115 caCom.XLogErr(ex.Message); 116 return null; 117 } 118 }
1 在使用DevExpress过程中,原先已经创建好的导航窗体,如何添加到DockPanel中进行展示? 2 3 FormX frmX = new FormX(); 4 5 frmX.Show(this.DockPanel1); 6 7 frmX.Dock = DockStyle.Fill; 8 9 frmX.TopLevel = false; 10 11 frmX.FormBorderStyle = FormBorderStyle.None; 12 13 this.DockPanel1.Text = frmX.Text; 14 15 this.DockPanel1.TabText = frmX.Text; 16 17 this.DockPanel1.Controls.Add(frmX); 18 19 这样处理即可,如果DockPanel是动态创建的。需要这样来写: 20 21 DockPanel panelX = new DockPanel(); 22 23 panelX = this.DockManager1.AddPanel(DockingStyle.Left); 24 25 然后将上面的DockPanel1替换成panelX即可。
试了以上几种方法还是没有达到效果,看来是我还没研究透