C#MDI子窗体通过TabControl列表显示的控制实现过程

news2024/12/28 12:38:47

类似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/2085430.html

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

相关文章

项目:基于TCP的文件传输系统

项目介绍: 模拟FTP原理&#xff1a;客户端连接服务器后&#xff0c;向服务器发送一个文件。文件名可以通过参数指定&#xff0c;服务器端接收客户端传来的文件&#xff08;文件名随意&#xff09;&#xff0c;如果文件不存在自动创建文件&#xff0c;如果文件存在&#xff0c;…

2024年整理的自动化测试面试题及答案

selenium中如何判断元素是否存在&#xff1f; 没有提供原生的方法判断元素是否存在&#xff0c;一般我们可以通过定位元素异常捕获的方式判断selenium中hidden或者是display &#xff1d; none的元素是否可以定位到&#xff1f;不可以&#xff0c;想点击的话&#xff0c;可以用…

C# 爬虫技术:京东视频内容抓取的实战案例分析

摘要 随着互联网技术的飞速发展&#xff0c;数据的获取和分析变得愈发重要。爬虫技术作为数据获取的重要手段之一&#xff0c;广泛应用于各个领域。本文将重点探讨C#语言在京东视频抓取中的实现过程&#xff0c;分析其技术细节&#xff0c;并提供相应的代码实现。 引言 京东…

python学习之路 - 面向对象编程

目录 一、面向对象编程1、成员方法a、类的定义和使用b、案例 2、类和对象3、构造方法4、其他内置方法&#xff08;魔术方法&#xff09;5、面向对象三大特性——封装a、介绍&#xff1a;b、表现形式&#xff1a;私有成员变量与私有成员方法c、作用 6、面向对象三大特性——继承…

iview Cascader 组件动态数据回显

在使用Cascader组件动态加载数据后&#xff0c;编辑的时候回显会有问题 问题如下&#xff1a;回显的时候&#xff0c;如果是多级&#xff0c;只显示了一级且&#xff0c;中间会闪一下 经过多方查找资料发现&#xff0c;是callback造成的。给组件增加on-visible-change事件监听…

如何下载淘宝的主图视频

目录&#xff1a; 1、通过插件插件下载短视频 1&#xff09;获取“Microsoft Edge扩展” 2&#xff09;搜索“aix智能下载器” 3&#xff09;将插件钉在浏览器上 4&#xff09;嗅控并下载视频 2、从其他来源安装插件 1、通过插件插件下载短视频 1&#xff09;获取“M…

孙宇晨:以区块链科技为翼,青年企业家引领社会进步新航向

​ 孙宇晨&#xff0c;作为区块链领域的一位青年企业家&#xff0c;以其大胆的创新精神和卓越的远见&#xff0c;正在用区块链技术推动社会的进步。他不仅在加密货币和区块链技术领域取得了令人瞩目的成就&#xff0c;还通过不断的努力&#xff0c;致力于将这些技术应用…

FreeRTOS 列表 List 源码解析

目录 一、链表及链表项的定义1、链表节点数据结构 xList_ITEM2、链表精简节点结构 xMINI_LIST_ITEM3、链表根节点结构 xLIST 二、链表的相关操作1、初始化1.1 链表节点初始化1.2 链表根节点初始化 2、插入2.1 将节点插入到链表的尾部2.2 将节点按照升序排列插入到链表 3、删除4…

(go)线性表的顺序存储

闲来无事&#xff0c;更新一下&#xff0c;线性表的顺序存储&#xff0c;go语言版本&#xff0c;效果都已经测试过&#xff0c;下面给出各部分细节 文章目录 1、生成一个线性表2、查找3、插入4、求长度5、改值6、删除7、遍历8、测试程序9、完整代码总结 package mainimport &q…

VBA技术资料MF195:屏蔽工作表中的粘贴输入

我给VBA的定义&#xff1a;VBA是个人小型自动化处理的有效工具。利用好了&#xff0c;可以大大提高自己的工作效率&#xff0c;而且可以提高数据的准确度。“VBA语言専攻”提供的教程一共九套&#xff0c;分为初级、中级、高级三大部分&#xff0c;教程是对VBA的系统讲解&#…

关于测试工程师在性能测试工具jmeter的熟悉和精通

经过一周的jmeter接口编写&#xff0c;不不不&#xff0c;是一年1-2次的jmeterd 使用&#xff0c;每次都是新的发现&#xff0c;新的起点&#xff01;&#xff01; 去年10月学习过的东西&#xff0c;现在还是不记得当时怎么这么聪明&#xff0c;于是&#xff0c;每次都是0基础…

笔试训练,牛客.合唱团牛客.kannan与高音牛客.拜访(BFS)牛客.买卖股票的最好时机(四)

目录 牛客.合唱团 牛客.kannan与高音 牛客.拜访&#xff08;BFS&#xff09; 牛客.买卖股票的最好时机(四) 牛客.合唱团 dp[i][j]:从1到i,中挑选最大乘积是多少&#xff0c;但是我们会发现状态转移方程推不出来&#xff0c;我们不知道如何推导的任意两个人&#xff0c; 从[…

[解决]Invalid configuration `aarch64-openwrt-linux‘: machine `aarch64-openwrt

背景 交叉编译libev-4.19 问题 checking host system type… Invalid configuration aarch64-openwrt-linux: machine aarch64-openwrt’ not recognized 解决 打开config.sub&#xff0c;在244行后添加"| aarch64-openwrt \ "

RK 方案u-boot阶段添加驱动

驱动部分&#xff1a; u-boot/drivers/video/drm/gpio_init.c /** (C) Copyright 2008-2017 Fuzhou Rockchip Electronics Co., Ltd** SPDX-License-Identifier: GPL-2.0*/#include <config.h> #include <common.h> #include <errno.h> #include <malloc…

jmeter连接mysql数据,并将查询结果存储到指定txt文件中

1、首先jmeter先进行连接mysql相关的配置&#xff0c;我之前已经有教程了就不赘述了&#xff0c;教程链接如下 jmeter连接mysql数据库以及常规用法-CSDN博客 2、当jmeter成功配置mysql数据库后&#xff0c;在JDBC Request组件中进行如下配置 Variable Name of Pool declared…

关于springboot对接通义千问大模型的尝试

今天正在路上刷手机&#xff0c;突然看到群里有人发了一个链接&#xff0c;内容是Spring Cloud Alibaba AI 的使用&#xff0c;spring cloud AI的使用&#xff0c;于是就想着玩一玩试试&#xff0c;难度不大&#xff0c;就是有些文档的坑&#xff0c;这里做一个记录&#xff0c…

基于RK3588+MCU智能清洁车应用解决方案

智能清洁车应用解决方案 在智慧城市建设发展的过程中&#xff0c;智慧环卫是打造智慧城市的重要组成部分&#xff0c;智能清洁车作为实现环卫智能化、提升作业效率和服务质量的关键工具&#xff0c;发挥着不可或缺的作用。 智能清洁车集成了激光雷达、双目视觉、多重传感器以及…

九月更新|用这5个简单技巧,新手在国内也能轻松使用ChatGPT,GPT新手使用手册(学术教师)

一、 ChatGPT可以做什么&#xff1f; 简单来说&#xff0c;ChatGPT就像一个超级智能的聊天机器人&#xff0c;它可以做很多事情。你可以把它想象成一个非常聪明的助手&#xff0c;随时随地帮你解答问题、提供建议、写文章、甚至讲笑话。以下是几个具体的例子&#xff1a; 1. …

论文浅尝 | 超越实体对齐: 通过实体关系协同实现完整的知识图谱对齐

笔记整理&#xff1a;米尔扎提阿力木&#xff0c;天津大学硕士&#xff0c;研究方向为大模型 论文链接&#xff1a;https://arxiv.org/abs/2407.17745 摘要 知识图谱对齐(Knowledge Graph Alignment, KGA)旨在整合来自多个来源的知识&#xff0c;以解决单个知识图谱在覆盖范围和…

一文带你了解可观测领域中APM与eBPF的技术差异

近年来&#xff0c;随着eBPF技术的兴起&#xff0c;很多人有这样的疑惑&#xff1a;eBPF和APM有什么区别&#xff1f;他们是竞争关系还是合作关系&#xff1f;本文将就此展开讨论&#xff0c;并给出切实有效的落地方案。 01APM APM全称&#xff1a;Application Performance Ma…