热门标签 | HotTags
当前位置:  开发笔记 > 运维 > 正文

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

在web页面上我们可以通过frameset,iframe嵌套框架很容易实现各种导航+内容的布局界面,而在winform、WPF中实现其实也很容易,通过本文给大家介绍在winform下实现左右布局多窗口界面的方法,本文介绍的非常详细,对winform布局相关知识感兴趣的朋友一起学习吧

在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();
}
private void ShowChildForm() 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() 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();
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();
}
private void ShowChildForm() 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();
menuItems.ToList().ForEach(m => m.Checked = false);
return menuItems.Where(m => m.Name == menuName).SingleOrDefault();
}
private void newToolStripMenuItem_Click(object sender, EventArgs e)
{
ShowChildForm();
}
}
} 

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

namespace WindowsFormsApplication1
{
partial class FormMain
{
/// 
/// 必需的设计器变量。
/// 
private System.ComponentModel.IContainer compOnents= null;
/// 
/// 清理所有正在使用的资源。
/// 
/// 如果应释放托管资源,为 true;否则为 false。
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows 窗体设计器生成的代码
/// 
/// 设计器支持所需的方法 - 不要
/// 使用代码编辑器修改此方法的内容。
/// 
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下实现左右布局多窗口界面的方法之续篇,感兴趣的朋友继续关注。


推荐阅读
  • 本文介绍了如何使用Postman构建和发送HTTP请求,包括四个主要部分:方法(Method)、URL、头部(Headers)和主体(Body)。特别强调了Body部分的重要性,并详细说明了不同类型的请求体。 ... [详细]
  • 面试题总结_2019年全网最热门的123个Java并发面试题总结
    面试题总结_2019年全网最热门的123个Java并发面试题总结 ... [详细]
  • CSS 百分比单位的取值依据是什么
    本文详细探讨了 CSS 中百分比单位的取值依据,包括不同定位方式下的包含块概念及其应用。通过具体的示例和代码,帮助读者更好地理解和掌握这一知识点。 ... [详细]
  • C# 实现可浮动工具栏功能
    本文介绍如何在 C# 中实现一个可浮动的工具栏,即工具栏可以从其初始位置拖出,并且可以重新拖回原位。通过创建一个新的窗口作为工具栏的容器,并利用 .NET Framework 提供的 ToolStrip 控件,可以轻松实现这一功能。 ... [详细]
  • C盘无法格式化的原因及解决方法
    本文探讨了C盘无法格式化的原因,并提供了详细的解决方案,帮助用户顺利进行系统维护。 ... [详细]
  • 开机自启动的几种方式
    0x01快速自启动目录快速启动目录自启动方式源于Windows中的一个目录,这个目录一般叫启动或者Startup。位于该目录下的PE文件会在开机后进行自启动 ... [详细]
  • XAMPP 遇到 404 错误:无法找到请求的对象
    在使用 XAMPP 时遇到 404 错误,表示请求的对象未找到。通过详细分析发现,该问题可能由以下原因引起:1. `httpd-vhosts.conf` 文件中的配置路径错误;2. `public` 目录下缺少 `.htaccess` 文件。建议检查并修正这些配置,以确保服务器能够正确识别和访问所需的文件路径。 ... [详细]
  • 在《Cocos2d-x学习笔记:基础概念解析与内存管理机制深入探讨》中,详细介绍了Cocos2d-x的基础概念,并深入分析了其内存管理机制。特别是针对Boost库引入的智能指针管理方法进行了详细的讲解,例如在处理鱼的运动过程中,可以通过编写自定义函数来动态计算角度变化,利用CallFunc回调机制实现高效的游戏逻辑控制。此外,文章还探讨了如何通过智能指针优化资源管理和避免内存泄漏,为开发者提供了实用的编程技巧和最佳实践。 ... [详细]
  • Vim 编辑器功能强大,但其默认的配色方案往往不尽如人意,尤其是注释颜色为蓝色时,对眼睛极为不友好。为了提升编程体验,自定义配色方案显得尤为重要。通过合理调整颜色,不仅可以减轻视觉疲劳,还能显著提高编码效率和兴趣。 ... [详细]
  • Linux基础知识:Vi与Vim编辑器详解
    Linux基础知识:Vi与Vim编辑器详解 ... [详细]
  • 如何在您的计算机上配置Python和PyCharm开发环境
    本文详细介绍了在Windows 10系统上配置Python和PyCharm开发环境的步骤。内容包括Python的安装与卸载、PyCharm的安装与卸载,以及如何在Windows 10中通过双击安装文件“python-3.7.2-amd64.exe”来完成Python的安装。此外,还提供了关于环境变量配置和基本设置的实用建议,帮助用户快速搭建高效的开发环境。 ... [详细]
  • 本文节选自《NLTK基础教程——用NLTK和Python库构建机器学习应用》一书的第1章第1.2节,作者Nitin Hardeniya。本文将带领读者快速了解Python的基础知识,为后续的机器学习应用打下坚实的基础。 ... [详细]
  • 大类|电阻器_使用Requests、Etree、BeautifulSoup、Pandas和Path库进行数据抓取与处理 | 将指定区域内容保存为HTML和Excel格式
    大类|电阻器_使用Requests、Etree、BeautifulSoup、Pandas和Path库进行数据抓取与处理 | 将指定区域内容保存为HTML和Excel格式 ... [详细]
  • 在JUnit测试框架中,确保@Test注解的方法按特定顺序执行是常见的需求。本文总结了三种实现这一目标的策略。首先,介绍了通过方法名称排序来控制执行顺序的基本方法。其次,推荐了一种利用依赖管理插件的方式,这种方法更为灵活且易于维护。最后,探讨了使用第三方库如TestNG或Jupiter扩展来实现更复杂的顺序控制。每种方法都有其适用场景和优缺点,开发者可以根据具体需求选择最合适的方案。 ... [详细]
  • 如何在方法上应用@ConfigurationProperties注解进行属性绑定 ... [详细]
author-avatar
向日葵一样2502932413
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有