在web頁面上我們可以通過frameset,iframe嵌套框架很容易實現各種導航+內容的布局界面,而在winform、WPF中實現其實也很容易,我這裡就分享一個:在winform下實現左右布局多窗口界面。
我這裡說的多窗口是指一個父窗口包含多個子窗口,在winform中實現這種效果很簡單,即將某個窗口的IsMdiContainer設為true,然後將其它子窗口的MdiParent設為其父窗口對象即可,這樣就完成了一個多窗口界面,效果如下:
點擊NEW新打開一個窗口,其效果如下:
請看我上圖紅色標注的地方,Windows菜單項下面顯示的是當前所有已打開的子窗口,點擊某個菜單,即可快速切換到其它窗口,若關閉某個子窗口,與之相對應的菜單項也會自動被移除,實現這個功能也很簡單,只需要將菜單的MdiWindowListItem屬性設為需要顯示活動窗口列表的菜單項即可,如:this.menuStrip1.MdiWindowListItem = this.WindowsToolStripMenuItem;
上述示例完整的實現代碼如下:
public
partial
class
FormMdi : Form
{
private
int
formCount = 0;
public
FormMdi()
{
InitializeComponent();
this
.menuStrip1.MdiWindowListItem =
this
.WindowsToolStripMenuItem;
}
private
void
newToolStripMenuItem_Click(
object
sender, EventArgs e)
{
ShowChildForm<FormChild>();
}
private
void
ShowChildForm<TForm>() where TForm : Form,
new
()
{
TForm childForm =
new
TForm();
childForm.Name =
"frm"
+ Guid.NewGuid().ToString(
"N"
);
childForm.Text =
string
.Format(
"Child Form -{0}"
, ++formCount);
childForm.MdiParent =
this
;
childForm.WindowState = FormWindowstate.Maximized;
childForm.Show();
}
}
相信實現上面這部份功能一般用過winform的人都會操作,我這裡就當是復習順便給新手一個參考,同時也為下面要實現的左右布局功能做一個鋪墊吧。
要實現左右布局,並且能夠支持可動態調整左右占比的功能,非SplitContainer控件莫屬了,如果不了解該控件用法請自行在網上查找相關資料,我這裡就不作說明,如果要顯示Windows已打開的子窗口情況,同樣也需要用到MenuStrip控件,
最終設計的主窗口(FormMain)效果如下:
我這裡因為只是演示,所以菜單控件上我只添加了兩個菜單項,分別為:WINDOWS,用於顯示Windows已打開的子窗口列表,NEW,用於打開一個子窗口;SplitContainer控件全部采用默認的,沒有放置任何控件在其中,如果用在正式系統中,一般左邊Panel1中會放置一個樹形菜單,右邊Panel2中保持空即可,因為這個是用來作為子窗口的容器。
界面設計好了,下面就實現最重要的兩個功能。
第一個功能:在右邊Panel2中顯示子窗口,實現代碼如下:
? 1 2 3 4 5 6 7 8 9 10 11 12 13 14public
FormMain()
{
this
.IsMdiContainer =
true
;
}
private
void
ShowChildForm<TForm>() where TForm : Form,
new
()
{
TForm childForm =
new
TForm();
childForm.Name =
"frm"
+ Guid.NewGuid().ToString(
"N"
);
childForm.Text =
string
.Format(
"Child Form -{0}"
, ++formCount);
childForm.MdiParent =
this
;
childForm.Parent = splitContainer1.Panel2;
childForm.WindowState = FormWindowstate.Maximized;
childForm.Show();
}
簡要說明:
1.在窗口構造函數中動態的將IsMdiContainer設為true,當然也可以設計視圖中設置;
2.編寫一個顯示寫子窗口的方法,方法中需注意的地方:childForm.MdiParent = this;childForm.Parent = splitContainer1.Panel2,意思是:將當前窗口作為子窗口的父窗口,同時將Panel2指定為子窗口的父對象,這樣就能實現子窗口在Panel2中打開了。
第二個功能:在Windows菜單項下顯示已打開的子窗口列表,這裡實現就沒有像文章一開始介紹的那樣簡單,使用那個方法是無效的,需要我們來自行實現,稍微有點復雜,但如果明白其實現原理,也就簡單明白了。
實現思路:當childForm加載到Panel2時,會觸發Panel2.ControlAdded事件,當childForm被關閉時,會觸發Panel2.ControlRemoved事件,我們可以統一訂閱這兩個事件,當childForm加載時,那麼就在Windows菜單項下增加一個菜單項,反之則移除該菜單項,實現代碼如下:
? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37this
.splitContainer1.Panel2.ControlAdded += Panel2_ControlChanged;
this
.splitContainer1.Panel2.ControlRemoved += Panel2_ControlChanged;
void
Panel2_ControlChanged(
object
sender, ControlEventArgs e)
{
var frm = e.Control
as
Form;
string
menuName =
"menu_"
+ frm.Name;
bool
exists =
this
.splitContainer1.Panel2.Controls.Contains(frm);
if
(exists)
{
var menuItem = GetMenuItem(menuName);
if
(menuItem !=
null
)
{
menuItem.Checked =
true
;
frm.BringToFront();
frm.Focus();
}
else
{
WindowsToolStripMenuItem.DropDownItems.Add(
new
ToolStripMenuItem() { Text = frm.Text, Name = menuName, Tag = frm, Checked =
true
});
}
}
else
{
var menuItem = GetMenuItem(menuName);
if
(menuItem !=
null
)
{
WindowsToolStripMenuItem.DropDownItems.Remove(menuItem);
menuItem.Dispose();
}
}
}
private
ToolStripMenuItem GetMenuItem(
string
menuName)
{
var menuItems = WindowsToolStripMenuItem.DropDownItems.Cast<ToolStripMenuItem>();
menuItems.ToList().ForEach(m => m.Checked =
false
);
return
menuItems.Where(m => m.Name == menuName).SingleOrDefault();
}
同時為了實現點擊WINDOWS菜單項的子菜單能夠快速切換子窗口,需要訂閱Windows菜單項的DropDownItemClicked事件,當然也可以為新增的子菜單項訂閱Click事件,實現代碼如下:
? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17windowsToolStripMenuItem.DropDownItemClicked += WindowsToolStripMenuItem_DropDownItemClicked;
void
WindowsToolStripMenuItem_DropDownItemClicked(
object
sender, ToolStripItemClickedEventArgs e)
{
var menuItem = GetMenuItem(e.ClickedItem.Name);
menuItem.Checked =
true
;
var childForm = menuItem.Tag
as
Form;
childForm.BringToFront();
childForm.Focus();
}
private
void
CheckWindowsMenuItem(
string
menuName)
{
var menuItem = GetMenuItem(menuName);
if
(menuItem !=
null
)
{
menuItem.Checked =
true
;
}
}
這樣就基本實現了在WINDOWS菜單項下顯示已打開的子窗口列表,並點擊指定的菜單項能夠切換當前活動的子窗口,但仍有一個不足的地方,那就是,當直接點擊子窗口來切換當前活動窗口時(說白了就是當點擊某個子窗口標題欄,該窗口就顯示在其它所有的子窗口最前面),Windows菜單項下的子菜單勾選項沒有同步更新,一開始想到的是用Activated事件來進行處理,結果經測試發現有效,該Activated事件在點擊子窗口標題欄時並不會被觸發,所以只能換種方法,經過多次測試,發現當窗口從後面切換到前面時(稱為Z順序改變),子窗口就會發生重繪,從而觸發Paint方法,我們可以訂閱該事件,並進行處理,實現代碼如下:
private
string
currentChildFormName =
null
;
//記錄當前活動子窗口名稱
childForm.Paint += (s, e) => {
var frm=s
as
Form;
if
(!frm.Name.Equals(currentChildFormName) &&
this
.splitContainer1.Panel2.Controls[0].Equals(frm))
//當容器中第一個控件就是當前的窗口,則表明該窗口處於所有窗口之上
{
CheckWindowsMenuItem(
"menu_"
+ frm.Name);
currentChildFormName = frm.Name;
}
};
最後貼出完整的實現代碼:
using
System;
using
System.Collections.Generic;
using
System.ComponentModel;
using
System.Data;
using
System.Drawing;
using
System.Linq;
using
System.Text;
using
System.Threading.Tasks;
using
System.Windows.Forms;
namespace
WindowsFormsApplication1
{
public
partial
class
FormMain : Form
{
private
int
formCount = 0;
private
string
currentChildFormName =
null
;
public
FormMain()
{
InitializeComponent();
this
.IsMdiContainer =
true
;
this
.splitContainer1.Panel2.ControlAdded += Panel2_ControlChanged;
this
.splitContainer1.Panel2.ControlRemoved += Panel2_ControlChanged;
windowsToolStripMenuItem.DropDownItemClicked += WindowsToolStripMenuItem_DropDownItemClicked;
}
void
WindowsToolStripMenuItem_DropDownItemClicked(
object
sender, ToolStripItemClickedEventArgs e)
{
var menuItem = GetMenuItem(e.ClickedItem.Name);
menuItem.Checked =
true
;
var childForm = menuItem.Tag
as
Form;
childForm.BringToFront();
childForm.Focus();
}
private
void
FormMain_Load(
object
sender, EventArgs e)
{
ShowChildForm<FormChild>();
}
private
void
ShowChildForm<TForm>() where TForm : Form,
new
()
{
TForm childForm =
new
TForm();
childForm.Name =
"frm"
+ Guid.NewGuid().ToString(
"N"
);
childForm.Text =
string
.Format(
"Child Form -{0}"
, ++formCount);
childForm.MdiParent =
this
;
childForm.Parent = splitContainer1.Panel2;
childForm.WindowState = FormWindowstate.Maximized;
childForm.Paint += (s, e) => {
var frm=s
as
Form;
if
(!frm.Name.Equals(currentChildFormName) &&
this
.splitContainer1.Panel2.Controls[0].Equals(frm))
//當容器中第一個控件就是當前的窗口,則表明該窗口處於所有窗口之上
{
CheckWindowsMenuItem(
"menu_"
+ frm.Name);
currentChildFormName = frm.Name;
}
};
childForm.Show();
}
private
void
CheckWindowsMenuItem(
string
menuName)
{
var menuItem = GetMenuItem(menuName);
if
(menuItem !=
null
)
{
menuItem.Checked =
true
;
}
}
void
Panel2_ControlChanged(
object
sender, ControlEventArgs e)
{
var frm = e.Control
as
Form;
string
menuName =
"menu_"
+ frm.Name;
bool
exists =
this
.splitContainer1.Panel2.Controls.Contains(frm);
if
(exists)
{
var menuItem = GetMenuItem(menuName);
if
(menuItem !=
null
)
{
menuItem.Checked =
true
;
frm.BringToFront();
frm.Focus();
}
else
{
WindowsToolStripMenuItem.DropDownItems.Add(
new
ToolStripMenuItem() { Text = frm.Text, Name = menuName, Tag = frm, Checked =
true
});
}
}
else
{
var menuItem = GetMenuItem(menuName);
if
(menuItem !=
null
)
{
WindowsToolStripMenuItem.DropDownItems.Remove(menuItem);
menuItem.Dispose();
}
}
}
private
ToolStripMenuItem GetMenuItem(
string
menuName)
{
var menuItems = WindowsToolStripMenuItem.DropDownItems.Cast<ToolStripMenuItem>();
menuItems.ToList().ForEach(m => m.Checked =
false
);
return
menuItems.Where(m => m.Name == menuName).SingleOrDefault();
}
private
void
newToolStripMenuItem_Click(
object
sender, EventArgs e)
{
ShowChildForm<FormChild>();
}
}
}
以下是系統自動生成的代碼:
namespace
WindowsFormsApplication1
{
partial
class
FormMain
{
/// <summary>
/// 必需的設計器變量。
/// </summary>
private
System.ComponentModel.IContainer components =
null
;
/// <summary>
/// 清理所有正在使用的資源。
/// </summary>
/// <param name="disposing">如果應釋放托管資源,為 true;否則為 false。</param>
protected
override
void
Dispose(
bool
disposing)
{
if
(disposing && (components !=
null
))
{
components.Dispose();
}
base
.Dispose(disposing);
}
#region Windows 窗體設計器生成的代碼
/// <summary>
/// 設計器支持所需的方法 - 不要
/// 使用代碼編輯器修改此方法的內容。
/// </summary>
private
void
InitializeComponent()
{
this
.menuStrip1 =
new
System.Windows.Forms.MenuStrip();
this
.WindowsToolStripMenuItem =
new
System.Windows.Forms.ToolStripMenuItem();
this
.newToolStripMenuItem =
new
System.Windows.Forms.ToolStripMenuItem();
this
.splitContainer1 =
new
System.Windows.Forms.SplitContainer();
this
.menuStrip1.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(
this
.splitContainer1)).BeginInit();
this
.splitContainer1.SuspendLayout();
this
.SuspendLayout();
//
// menuStrip1
//
this
.menuStrip1.Items.AddRange(
new
System.Windows.Forms.ToolStripItem[] {
this
.WindowsToolStripMenuItem,
this
.newToolStripMenuItem});
this
.menuStrip1.Location =
new
System.Drawing.Point(0, 0);
this
.menuStrip1.MdiWindowListItem =
this
.WindowsToolStripMenuItem;
this
.menuStrip1.Name =
"menuStrip1"
;
this
.menuStrip1.Size =
new
System.Drawing.Size(1069, 25);
this
.menuStrip1.TabIndex = 1;
this
.menuStrip1.Text =
"menuStrip1"
;
//
// WindowsToolStripMenuItem
//
this
.WindowsToolStripMenuItem.Name =
"WindowsToolStripMenuItem"
;
this
.WindowsToolStripMenuItem.Size =
new
System.Drawing.Size(73, 21);
this
.WindowsToolStripMenuItem.Text =
"Windows"
;
this
.WindowsToolStripMenuItem.Click +=
new
System.EventHandler(
this
.WindowsToolStripMenuItem_Click);
//
// newToolStripMenuItem
//
this
.newToolStripMenuItem.Name =
"newToolStripMenuItem"
;
this
.newToolStripMenuItem.Size =
new
System.Drawing.Size(46, 21);
this
.newToolStripMenuItem.Text =
"New"
;
this
.newToolStripMenuItem.Click +=
new
System.EventHandler(
this
.newToolStripMenuItem_Click);
//
// splitContainer1
//
this
.splitContainer1.BackColor = System.Drawing.SystemColors.ActiveCaption;
this
.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill;
this
.splitContainer1.Location =
new
System.Drawing.Point(0, 25);
this
.splitContainer1.Name =
"splitContainer1"
;
//
// splitContainer1.Panel2
//
this
.splitContainer1.Panel2.BackColor = System.Drawing.SystemColors.ScrollBar;
this
.splitContainer1.Size =
new
System.Drawing.Size(1069, 526);
this
.splitContainer1.SplitterDistance = 356;
this
.splitContainer1.TabIndex = 2;
//
// FormMain
//
this
.AutoScaleDimensions =
new
System.Drawing.SizeF(6F, 12F);
this
.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this
.ClIEntSize =
new
System.Drawing.Size(1069, 551);
this
.Controls.Add(
this
.splitContainer1);
this
.Controls.Add(
this
.menuStrip1);
this
.MainMenuStrip =
this
.menuStrip1;
this
.Name =
"FormMain"
;
this
.Text =
"FormMain"
;
this
.Load +=
new
System.EventHandler(
this
.FormMain_Load);
this
.menuStrip1.ResumeLayout(
false
);
this
.menuStrip1.PerformLayout();
((System.ComponentModel.ISupportInitialize)(
this
.splitContainer1)).EndInit();
this
.splitContainer1.ResumeLayout(
false
);
this
.ResumeLayout(
false
);
this
.PerformLayout();
}
#endregion
private
System.Windows.Forms.MenuStrip menuStrip1;
private
System.Windows.Forms.ToolStripMenuItem WindowsToolStripMenuItem;
private
System.Windows.Forms.SplitContainer splitContainer1;
private
System.Windows.Forms.ToolStripMenuItem newToolStripMenuItem;
}
}
以下是效果演示截圖:
以上內容給大家分享了在winform下實現左右布局多窗口界面的方法,有什麼更好的實現方法可以在下方評論,不足之處也歡迎指出,謝謝!