C#中通过TabControl控制MDI子窗体显示切换的实现过程

news2024/9/26 1:18:48

类似excel表格中各个表单sheet的切换效果,使用tabcontrol控件实现类似的功能。效果如下:

过程涉及父窗体MDIParent1、子窗体main、自定义基础功能类MdiChildBase。

基础功能类MdiChildBase继承自Form创建,定义了一个委托SetTabControlDelegate,委托的实例SetTabI用于指定FormClosing和Shown方法中调用的方法(函数),添加了窗口的FormClosing和Shown处理方法,窗口再打开和关闭的时候会被调用,即后面继承MdiChildBase创建的子窗体会在窗口关闭和显示的时候执行相应方法。可见,委托的实例SetTabI起到重要的连接作用。

子窗体main是自己创建的窗体,继承自类MdiChildBase,其他用于子窗体的Form也需要继承类MdiChildBase:“public partial class Main : MdiChildBase”,在窗口的关闭和显示过程中会执行MdiChildBase中注册的FormClosing和Shown方法,调用父窗体MDIParent1的SetTabControl方法添加和移除对应窗口的tab标签;

父窗体MDIParent1为MDI父窗体,包含一个用户显示子窗体列表的tabcontrol控件,定义了SetTabControl方法添加和移除对应窗口的tab标签;

主要代码

父窗体MDIParent1:

MDIParent父窗体设置为MDI容器窗体:this.IsMdiContainer = true;

MDIParent父窗体的Designer中tabControl控件的主要属性设置:

// 
// tabControl1
// 
this.tabControl1.Dock = System.Windows.Forms.DockStyle.Bottom;
this.tabControl1.Location = new System.Drawing.Point(0, 496);
this.tabControl1.Name = "tabControl1";
this.tabControl1.SelectedIndex = 0;
this.tabControl1.ShowToolTips = true;
this.tabControl1.Size = new System.Drawing.Size(992, 18);
this.tabControl1.TabIndex = 4;
this.tabControl1.Visible = false;
this.tabControl1.SelectedIndexChanged += new System.EventHandler(this.tabControl1_SelectedIndexChanged);

MDIParent:

public partial class MDIParent1 : Form
{
	private void MDIParent1_Load(object sender, EventArgs e)
	{
		MdiChildBase.SetTabII = this.SetTabControl;
		LoadMdiForm(typeof(Main));
	}

	public void LoadMdiForm(Type formType)
	{
		Form w = Application.OpenForms[formType.Name];
		
		bool exists = false;
		MdiChildBase mdiChild = null;
		if (w != null)
		{
			exists = true;
			mdiChild = w as MdiChildBase;
		}

		if (!exists)
		{
			mdiChild = (MdiChildBase)Activator.CreateInstance(formType, true);
			mdiChild.MdiParent = this;
			mdiChild.Show();
		}

		mdiChild.SetTabI = SetTabControl;
		mdiChild.BringToFront();
		mdiChild.Activate();
	}

	public void SetTabControl(string type,string name)
	{ 
		if(type=="add")
		{
			TabPage page= new TabPage();
			page.Text = name;
			page.Name = name;
			
			tabControl1.TabPages.Add(page);
			tabControl1.SelectedTab = page;
			if (tabControl1.TabPages.Count > 0) tabControl1.Visible = true ;
		}
		else if (type=="remove")
		{
			TabPage tab = tabControl1.TabPages[name];
			if(tab != null)
				tabControl1.TabPages.Remove(tab);

			if (tabControl1.TabPages.Count == 0) tabControl1.Visible = false;
		}
		else if (type=="select")
		{
			TabPage page = tabControl1.TabPages[name];
			tabControl1.SelectedTab = page;
		}
	}

	private void tabControl1_SelectedIndexChanged(object sender, EventArgs e)
	{
		if (tabControl1.SelectedTab == null) return;
		string WindowsName = tabControl1.SelectedTab.Text;
		// MdiChildren
		Form w = Application.OpenForms[WindowsName];
		//判断对应窗口是否被打开
		//if ((w == null) || (w.IsDisposed))
		w.Activate();
		//w.Show();
		//w.WindowState = FormWindowState.Normal;
	}
}

子窗体main:

public partial class Main : MdiChildBase
{
    public Main()
    {
        CheckForIllegalCrossThreadCalls = false;
        InitializeComponent();
    }

    private void Form1_Load(object sender, EventArgs e)
    {

    }
}

 MdiChildBase:

public class MdiChildBase:Form
{
    public MdiChildBase()
    {
        this.FormClosing += new FormClosingEventHandler(baseFormClosing);
        this.Shown += new EventHandler(baseFormShown);
    }

    /// <summary>
    /// 主要用于MDI父窗体中窗口列表的显示
    /// </summary>
    /// <param name="type"></param>
    /// <param name="name"></param>
    public delegate void SetTabControlDelegate(string type, string name);
    //用于从mdiparent主窗口发起的窗口FormClosing、Shown过程中对窗口列表的添加删除;
    public SetTabControlDelegate SetTabI;
    //用于从其他非mdiparent主窗口发起的窗口生成,在mdiparent主窗口load中设置了方法名称;
    //即:mdiparent主窗口实例存在的话,会在子窗口中的FormClosing和Shown过程中执行对应的方法;
    //    若主窗口不是从 mdiparent主窗口开始运行,则不存在MDI父窗体,SetTabII为null,也不会执行对应方法
    public static SetTabControlDelegate SetTabII;   

    //public Action<string,string> SetTabI;
    //public static Action<string, string> SetTabII;
    public virtual void baseFormClosing(object sender, EventArgs e)
    {
        //SetTabI?.Invoke("remove", this.Name);
        if (SetTabI != null)
        {
            SetTabI.Invoke("remove", this.Name);
            //MessageBox.Show("remove");
        }
    }

    public virtual void baseFormShown(object sender, EventArgs e)
    {
        //SetTabI?.Invoke("add", this.Name);
        if (SetTabI != null)
        {
            SetTabI.Invoke("add", this.Name);
            //MessageBox.Show("add");
        }
    }

}

代码复用问题

上面所说的是针对父窗体包含子窗体的使用方式,在实际中会存在一个窗体可用于子窗体,也可用于单独窗体或单独项目中的启动窗体,如下面的窗体“数据对比”,它继承自MdiChildBase创建,当它用于一个不包含MDIParent的项目中(不包含对MDIParent的引用,因为MDIParent做为主窗体,可能所在项目包含了对所有窗体/类的引用),可借助MdiChildBase中的委托实例SetTabII来进行区分,具体信息见MdiChildBase类中SetTabII的文字说明。

//Form f = Application.OpenForms["数据对比"];
数据对比 f = new 数据对比(dt);
Form mDIParent = Application.OpenForms["MDIParent1"];
MDIParent1 mDIParent1 = Application.OpenForms["MDIParent1"] as MDIParent1;

MdiChildBase mdiChild = null;
if (f != null)
{
    f.MdiParent = mDIParent;
    mdiChild = f as MdiChildBase;
}
else
{
    mdiChild = (MdiChildBase)Activator.CreateInstance(typeof(数据对比), true);
    mdiChild.MdiParent = mDIParent;
}

mdiChild.SetTabI = MdiChildBase.SetTabII;
mdiChild.Show();
mdiChild.BringToFront();
mdiChild.Activate();

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2089592.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

apisix 本地开发环境部署

apisix 本地开发环境部署 本地开发环境部署可以采用 docker-compose 部署&#xff0c;配置文件如下 apisix 配置文件 apisix:node_listen: 9080 # APISIX 节点监听地址enable_ipv6: falsehttp:port: 9080 # APISIX HTTP 端口#https:# port: 9443 # APISIX HTTPS 端口# ssl…

《机器学习》K-means 聚类 原理、参数解析、案例实现

1. 引言 随着数据的快速增长和复杂性的不断提高,如何从大量数据中提取有用信息已成为数据科学和人工智能领域的核心挑战。聚类作为一种无监督学习方法,通过将数据分为若干组,使得同一组内的样本具有较高的相似性,而不同组之间的样本差异显著。这种方法被广泛应用于数据分析…

探索极速Python:Sanic框架的魔力

文章目录 探索极速Python&#xff1a;Sanic框架的魔力背景&#xff1a;为什么选择Sanic&#xff1f;Sanic是什么&#xff1f;如何安装Sanic&#xff1f;简单的库函数使用方法场景应用示例常见Bug及解决方案总结 探索极速Python&#xff1a;Sanic框架的魔力 背景&#xff1a;为什…

带你了解RS485通讯网关-天拓四方

在当今工业自动化和智能化的浪潮中&#xff0c;高效、可靠的数据通讯是确保系统运行顺畅的关键。RS485通讯网关作为工业通讯网络中的核心设备&#xff0c;承担着数据传输的重要任务。本文将从RS485通讯网关的技术背景、功能特性、应用场景以及选购要点等方面进行深入探讨&#…

【智能算法应用】基于融合改进A星-麻雀搜索算法求解六边形栅格地图路径规划

目录 1.算法原理2.结果展示3.参考文献4.代码获取 1.算法原理 【智能算法】麻雀搜索算法&#xff08;SSA&#xff09;原理及实现 六边形栅格地图 分析一下地图&#xff1a; 六边形栅格地图上移动可以看做6领域运动&#xff0c;偶数列与奇数列移动方式有所差异&#xff0c;将六…

[每周一更]-(第112期):WDL(Workflow Description Language)说明及使用示例

文章目录 什么是WDL&#xff1f;WDL的基本结构示例 WDL 文件示例任务&#xff1a;字符串反转示例工作流&#xff1a;字符串处理 WDL 文件详解任务&#xff08;Task&#xff09;工作流&#xff08;Workflow&#xff09; 运行示例总结报错参考 什么是WDL&#xff1f; WDL&#x…

Java图形用户界面之Applet设计

Applet设计 前言一、Applet的基本工作原理与使用局限Applet的基本工作原理代码示例 Java Applet 的使用局限Java沙箱安全机制原理 Applet的生命周期与运行方式 二、Applet类Applet类的层次结构常用方法生命周期方法显示方法多媒体支持方法其他方法 三、Applet和GUI基于AWT和Swi…

字符操作函数和内存操作函数

1 字符操作函数 1.strlen size_t strlen ( const char * str ); 函数功能&#xff1a;返回以\0结尾的字符串长度&#xff0c;不包含\0。 注&#xff1a;strlen返回值是一个无符号整数size_t。 #include <stdio.h>int main() {const char*str1 "abcdef";const…

Linux基础软件-selinux库文件swap

作者介绍&#xff1a;简历上没有一个精通的运维工程师。希望大家多多关注作者&#xff0c;下面的思维导图也是预计更新的内容和当前进度(不定时更新)。 Linux进阶部分又分了很多小的部分,我们刚讲完了Linux日常运维。讲的那些东西都算是系统自带的&#xff0c;但是Linux作为一个…

LVGL 控件之圆弧(lv_arc)

目录 一、圆弧部件1、部件组成2、lv_art_t3、圆弧部件角度设置4、圆弧部件旋转设置5、圆弧的模式选择6、圆弧部件的变化率设置7、移除旋钮8、事件9、获取/设置信息相关的 API 二、例程 一、圆弧部件 1、部件组成 圆弧&#xff08;lv_arc&#xff09;部件由三个部分组成&#…

学习之SQL语言之DDL

查询 查询所有数据 SHOW DATABASES&#xff1b; 查询当前数据库 SELECT DATABASE(); 创建 CREATE DATABASE IF NOT EXISTS 数据库名&#xff1b; 删除 DROP DATABASE IF EXISTS 数据库名&#xff1b; 使用 USE 数据库名&#xff1b; 查询当前数据库所有表 SHOW TABLES; 查…

优化大型语言模型微调:MoLA层级专家分配策略

人工智能咨询培训老师叶梓 转载标明出处 大模型&#xff08;LLMs&#xff09;的微调过程中&#xff0c;计算资源的需求巨大&#xff0c;这促使研究者们探索参数高效微调&#xff08;PEFT&#xff09;技术。低秩适应&#xff08;LoRA&#xff09;和专家混合模型&#xff08;MoE…

超分 Real-ESRGAN 使用笔记

效果图 目录 依赖项: 视频推理入口: 图片推理入口文件: RealESRGAN_x4plus 12g显存不够 RealESRGAN_x4plus_anime_6B 的效果 树枝之间产生了蒙版 RealESRNet_x4plus 有点模糊 2022年开源的 GitHub - xinntao/Real-ESRGAN: Real-ESRGAN aims at developing Practical…

操作系统 --王道计算机考研--学习笔记

文章目录 前言第一章 引言1.1 什么是操作系统&#xff1f;1 操作系统作为虚拟机2 操作系统作为资源管理者3 为上层提供服务-- 系统调用 2.操作系统特征与发展分类2.1 操作系统特征1.并发2.共享3.虚拟4.异步 2.2 操作系统发展和分类 3.操作系统的的运行机制和中断异常、系统调用…

vue脚手架的创建

一、安装node环境 切换阿里云镜像 npm config set registry https://registry.npm.taobao.org 2、全局安装vue-cli和webpack 已经安装过node.js之后和淘宝镜像的话&#xff0c;vue的运行环境基本上就搭建好了&#xff0c;只需再安装全局的webpack&#xff08;命令行&#…

【大模型理论篇】RoPE旋转位置编码底层数学原理分析

1. 位置编码对于NLP模型的作用 位置编码&#xff08;Positional Encoding&#xff09;在大模型&#xff08;例如Transformer架构&#xff09;中起到了非常重要的作用。没有位置编码的信息&#xff0c;模型会丧失序列的顺序信息&#xff0c;导致模型退化成一个简单的“词…

物品租赁​​​​​​​|基于SprinBoot+vue的物品租赁​​​​​​​系统(源码+数据库+文档)

物品租赁系统 基于SprinBootvue的物品租赁系统 一、前言 二、系统设计 三、系统功能设计 5.1 系统功能模块 5.2 管理员功能模块实现 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&#xff1a; 博主介绍&#xff1a;✌️大…

【单片机原理及应用】实验:数码管的中断控制

目录 一、实验目的 二、实验内容 三、实验步骤 四、记录与处理 五、思考 六、成果文件提取链接 一、实验目的 掌握外部中断的工作原理&#xff0c;学习中断编程与程序调试方法。 二、实验内容 【参照图表】 &#xff08;1&#xff09;创建一个包含80C51固件&#xff0c;采用…

汽车乘客热舒适度大挑战,如何利用仿真技术提高汽车环境舒适度

舒适性在人们选择汽车的决定性方面占比越来越重&#xff0c;而汽车乘员舱环境的舒适性是指为乘员提供舒适愉快便利的乘坐环境与条件&#xff0c;包括良好的平顺性、车内的低噪声、适宜的空气环境以及良好的驾驶操作性能。 舒适性 经济性 安全性、动力性 典型的乘员舱热舒适性模…

git常见命令行及分支规范

文章目录 GIT常见命令行原理图基本设置初始化和克隆仓库文件管理提交更改查看状态和历史分支管理远程仓库交互高级功能GIT常见分支风格1. 单一主干分支(Single Main Branch)//极少使用优点:缺点:2. 多主干分支(Multiple Main Branches)//个人小型项目采用优点:缺点:3. …