在winform下实现左右布局多窗口界面的方法

时间:2021-12-24 05:21:01

在web页面上我们可以通过frameset,iframe嵌套框架很容易实现各种导航+内容的布局界面,而在winform、wpf中实现其实也很容易,我这里就分享一个:在winform下实现左右布局多窗口界面。

我这里说的多窗口是指一个父窗口包含多个子窗口,在winform中实现这种效果很简单,即将某个窗口的ismdicontainer设为true,然后将其它子窗口的mdiparent设为其父窗口对象即可,这样就完成了一个多窗口界面,效果如下:

在winform下实现左右布局多窗口界面的方法

点击new新打开一个窗口,其效果如下:

在winform下实现左右布局多窗口界面的方法

请看我上图红色标注的地方,windows菜单项下面显示的是当前所有已打开的子窗口,点击某个菜单,即可快速切换到其它窗口,若关闭某个子窗口,与之相对应的菜单项也会自动被移除,实现这个功能也很简单,只需要将菜单的mdiwindowlistitem属性设为需要显示活动窗口列表的菜单项即可,如:this.menustrip1.mdiwindowlistitem = this.windowstoolstripmenuitem;

上述示例完整的实现代码如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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)效果如下:

在winform下实现左右布局多窗口界面的方法

我这里因为只是演示,所以菜单控件上我只添加了两个菜单项,分别为:windows,用于显示windows已打开的子窗口列表,new,用于打开一个子窗口;splitcontainer控件全部采用默认的,没有放置任何控件在其中,如果用在正式系统中,一般左边panel1中会放置一个树形菜单,右边panel2中保持空即可,因为这个是用来作为子窗口的容器。

控件层次结构如下图示:

在winform下实现左右布局多窗口界面的方法

界面设计好了,下面就实现最重要的两个功能。

第一个功能:在右边panel2中显示子窗口,实现代码如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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
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
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
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
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>();
}
}
}

以下是系统自动生成的代码:

?
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
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下实现左右布局多窗口界面的方法

以上内容给大家分享了在winform下实现左右布局多窗口界面的方法,有什么更好的实现方法可以在下方评论,不足之处也欢迎指出,谢谢!下面将给大家介绍,感兴趣的朋友继续关注。