此功能在借鑒以下鏈接博文后驗證實現,特此感謝,並做補充轉發分享!
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(); } } }