在web页面上我们可以通过frameset,iframe嵌套框架很容易实现各种导航+内容的布局界面,而在winform、WPF中实现其实也很容易,我这里就分享一个:在winform下实现左右布局多窗口界面。
我这里说的多窗口是指一个父窗口包含多个子窗口,在winform中实现这种效果很简单,即将某个窗口的IsMdiContainer设为true,然后将其它子窗口的MdiParent设为其父窗口对象即可,这样就完成了一个多窗口界面,效果如下:
点击NEW新打开一个窗口,其效果如下:
请看我上图红色标注的地方,Windows菜单项下面显示的是当前所有已打开的子窗口,点击某个菜单,即可快速切换到其它窗口,若关闭某个子窗口,与之相对应的菜单项也会自动被移除,实现这个功能也很简单,只需要将菜单的MdiWindowListItem属性设为需要显示活动窗口列表的菜单项即可,如:this.menuStrip1.MdiWindowListItem = this.windowsToolStripMenuItem;
上述示例完整的实现代码如下:
相信实现上面这部份功能一般用过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
14
15
16
|
public
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
37
38
39
40
|
this
.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
17
18
19
20
21
|
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
CheckWindowsMenuItem(
string
menuName)
{
var
menuItem = GetMenuItem(menuName);
if
(menuItem !=
null
)
{
menuItem.Checked =
true
;
}
}
|
这样就基本实现了在WINDOWS菜单项下显示已打开的子窗口列表,并点击指定的菜单项能够切换当前活动的子窗口,但仍有一个不足的地方,那就是,当直接点击子窗口来切换当前活动窗口时(说白了就是当点击某个子窗口标题栏,该窗口就显示在其它所有的子窗口最前面),WINDOWS菜单项下的子菜单勾选项没有同步更新,一开始想到的是用Activated事件来进行处理,结果经测试发现有效,该Activated事件在点击子窗口标题栏时并不会被触发,所以只能换种方法,经过多次测试,发现当窗口从后面切换到前面时(称为Z顺序改变),子窗口就会发生重绘,从而触发Paint方法,我们可以订阅该事件,并进行处理,实现代码如下:
1
2
3
4
5
6
7
8
9
10
|
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;
}
};
|
最后贴出完整的实现代码:
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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
|
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>();
}
}
}
|
以下是系统自动生成的代码:
以下是效果演示截图:
如果大家有什么更好的实现方法可以在下方评论,不足之处也欢迎指出,谢谢!
本文转自 梦在旅途 博客园博客,原文链接:http://www.cnblogs.com/zuowj/p/5092038.html ,如需转载请自行联系原作者