在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中显示子窗口,实现代码如下:

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菜单项下增加一个菜单项,反之则移除该菜单项,实现代码如下:

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事件,实现代码如下:

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方法,我们可以订阅该事件,并进行处理,实现代码如下:

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();}#endregionprivate System.Windows.Forms.MenuStrip menuStrip1;private System.Windows.Forms.ToolStripMenuItem windowsToolStripMenuItem;private System.Windows.Forms.SplitContainer splitContainer1;private System.Windows.Forms.ToolStripMenuItem newToolStripMenuItem;}
}

以下是效果演示截图:

如果大家有什么更好的实现方法可以在下方评论,不足之处也欢迎指出,谢谢!

转载于:https://www.cnblogs.com/zuowj/p/5092038.html

分享在winform下实现左右布局多窗口界面相关推荐

  1. Winform下的地图开发控件(GMap.NET)使用心得之二

    在上篇<Winform下的地图开发控件(GMap.NET)使用心得>中简单介绍了GMap.NET的控件基本情况,本篇开始介绍一下相关的代码操作. 其实目前GMap.NET提供的功能还不是很 ...

  2. WinForm下ComboBox获取绑定对象集的SelectedValue补充

    在上文<WinForm下ComboBox设定SelectedValue总结>中,我列举了出现不能正常获取SlectedValue的一些方法.原文写得比较乱,引起读者的理解分歧,在此表示歉意 ...

  3. java messagebox 关闭_wince/WinForm下实现一个自动关闭的MessageBox

    WinForm 下我们可以调用MessageBox.Show 来显示一个消息对话框,提示用户确认等操作.在有些应用中我们需要通过程序来自动关闭这个消息对话框而不是由用户点击确认按钮来关闭.然而.Net ...

  4. winform下的未捕捉的异常处理

    winform下可以有两种方式来处理未捕捉的异常: 1.通过挂接Application.ThreadException 事件来处理未捕捉的异常. 2.通过挂接AppDomain.UnhandledEx ...

  5. WinForm下DataGridView导出Excel的实现

    WinForm下DataGridView导出Excel的实现   1.说明:导出的效率说不上很高,但至少是可以接收的.参考网上很多高效导出Excel的方法,实现到时能够实现的,导出速度也很快,不过缺陷 ...

  6. WinForm 下实现一个自动关闭的MessageBox

    WinForm 下实现一个自动关闭的MessageBox Author: eaglet WinForm 下我们可以调用MessageBox.Show 来显示一个消息对话框,提示用户确认等操作.在有些应 ...

  7. Android开发笔记(一百二十三)下拉刷新布局SwipeRefreshLayout

    SwipeRefreshLayout 下拉刷新布局SwipeRefreshLayout是Android又一与时俱进的控件,顾名思义它随着用户手势向下滑动就会触发刷新操作.从实际的下拉效果来看,Swip ...

  8. arch linux键盘布局,达内培训之更改ArchLinux终端下的键盘布局

    下面为大家简单介绍如何更改ArchLinux终端下的键盘布局 archlinux wiki 上的直接引用 cd 到 /usr/share/kbd/keymaps/i386/qwerty 将默认键盘 ( ...

  9. C#下Winform下使用WebKit、Geckofx、CefSharp对比及CefSharp代码实现

    C#Winform下使用WebKit.Geckofx.CefSharp对比及CefSharp代码实现 一.使用visual studio 2021自带的NuGet程序包管理器安装CefSharp 二. ...

最新文章

  1. poj 2681 字符串
  2. Spring Boot“内存泄漏”?看看美团大牛是如何排查的
  3. 分享这篇耗子叔的-《请玉伯一起来聊一聊“所向无敌的土方法”》
  4. ivew 双向绑定时间控件
  5. UA SIE545 优化理论基础1 凸分析3 凸集与凸包
  6. 03、Swagger2和Springmvc整合详细记录(爬坑记录)
  7. QT5运行错误:ImportError: cannot import name QAxContainer from PyQt5 解决
  8. Java面试2018常考题目汇总
  9. python面向对象编程之组合
  10. 服务器多路径协议,多主机多路径分流传输协议研究与设计
  11. LeetCode 82 删除排序链表中的重复元素 II python
  12. Xml xpath samples
  13. [OJ] Wildcard Matching (Hard)
  14. gradle创建web工程_Gradle入门:创建Web应用程序项目
  15. 搭建一台本地json服务器
  16. 解决复杂多数据源报表的5种通用办法
  17. linux如何安装infer
  18. 代码精进之路读后感(三)
  19. php preview,preview.php
  20. shell 脚本运算符

热门文章

  1. IAR编译器的常见问题
  2. Sql Server使用链接服务器远程取数据!
  3. java注解方式实体类_如何用注解的方式在实体类中实现一对一,和一对多多对多...
  4. 基2频率抽取实现FFT的Verilog程序
  5. 基于MATLAB FDATOOL的CIC滤波器设计
  6. 计算机虚拟内存的设置
  7. C++中相对路径和绝对路径
  8. 国产系统安装安卓应用教程
  9. python  字典 元组 集合 列表 字符串 字节数组 常用的方法总结
  10. Java四种引用简介