C# Winform 多窗体切换方式二

一、简介

在 Winform 开发中,多窗体的切换是一个常见的需求,比如登录成功后,切换至主界面,同时关闭登录界面,看似简单的需求,如果在别的框架里是很容易实现的,在 Winform 你如果按同样的逻辑写  this.Close(); form2.Show(); 你就会发现软件直接闪退了,因为 Winform 存在一个主线程概念,如果主线程关闭,那么当前软件所有的窗体都会随之关闭,这时你就会去查相关的资料,你会发现有的用的  form2.Show();  this.Hide(); 这种方式,这种方式其实也不是特别好,切换一两个窗体还好,如果窗体太多了,那代码就非常的难看。

在不久前,我写了一篇多窗体切换相关的帖子,地址如下:

C# Winform 多窗体切换方式一_winform登录成功后转向另一个窗体语句-优快云博客

有兴趣的可以去看看,这篇帖子用的方式是用的用户控件在窗体里面切换,并自动改变窗体的大小,从而看上去像切换了窗体一样,原理也非常的简单。

今天介绍多窗体切换的第二种,用一个窗体作为主窗体,启动后隐藏,并添加一个窗体的管理类,进行窗体的切换、开启或关闭。

效果:

二、实现效果

新建一个类库,取名 FormManager,添加一个窗体,取名 _MainForm,界面如下:

它本来就是没有任何界面,这个只是用来作为主线程,在后台运行,用来防止程序关闭用的。

代码:

using System;
using System.Windows.Forms;

public partial class _MainForm : Form
{
    /// <summary>
    /// 主程序启动后执行自定义的委托
    /// </summary>
    /// <param name="action"></param>
    public _MainForm(Action action)
    {
        InitializeComponent();
        StartAction = action;
    }

    /// <summary>
    /// 主程序启动后显示自定义的界面
    /// </summary>
    /// <param name="formType"></param>
    public _MainForm(Type formType)
    {
        InitializeComponent();

        if(formType == null) 
            throw new ArgumentNullException("参数 formType 不能为空");
        if (!typeof(Form).IsAssignableFrom(formType))
            throw new ArgumentException($"当前参数 {formType.Name} 不是 Form 的派生类", nameof(formType));

        this.Form1 = formType;
    }

    private Action StartAction;
    private Type Form1;

    private void _MainForm_Load(object sender, EventArgs e)
    {
        this.WindowState = FormWindowState.Minimized;
        this.ShowInTaskbar = false;
        this.Visible = false;

        //显示第一个窗体
        if (this.Form1 != null)
            FormManager.OpenForm(Form1);

        StartAction?.Invoke();
    }
}

这里有两个构造函数,第一个构造函数是传入一个委托,并在界面启动后,调用这个委托,你可以把启动那个界面,写在这个委托里,第二个构造函数是传入一个 Form 的 Type 作为显示的第一个程序。

添加一个类 FormManager.cs,这个类用来管理所有 Form 的启动和关闭。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;

/// <summary>
/// 窗体管理
/// </summary>
public class FormManager
{
    #region 字段

    //所有打开的窗体
    private static Dictionary<Type, Form> _openForms = new Dictionary<Type, Form>();
    //主窗体
    private static _MainForm _mainForm;

    /// <summary>
    /// 是否显示控制台日志
    /// </summary>
    public static bool IsShowErrorLog { get; set; }
    /// <summary>
    /// 错误日志委托
    /// </summary>
    public static event Action<string> ErrorLogAction;

    #endregion

    #region 主窗体

    /// <summary>
    /// 主窗体 实例化 / 获取实例 
    /// </summary>
    /// <typeparam name="K">第一个窗体的类型</typeparam>
    /// <returns>主窗体的实例</returns>
    public static _MainForm MainFormControl<K>() where K : Form, new()
    {
        if (_mainForm == null)
        {
            Type formType = typeof(K);
            _mainForm = new _MainForm(formType);
            return _mainForm;
        }
        return _mainForm;
    }

    /// <summary>
    /// 主窗体 实例化 / 获取实例 
    /// </summary>
    /// <param name="action"></param>
    /// <returns>主窗体的实例</returns>
    public static _MainForm MainFormControl(Action action)
    {
        if (_mainForm == null)
        {
            _mainForm = new _MainForm(action);
            return _mainForm;
        }
        return _mainForm;
    }

    /// <summary>
    /// 关闭主窗体
    /// </summary>
    public static void CloseMainForm()
    {
        if (_mainForm == null)
        {
            AddErrorLog("[CloseMainForm]字段 _mainForm 不能为空");
            return;
        }

        if (_mainForm.InvokeRequired)
            _mainForm.Invoke(new Action(()=>_mainForm.Close()));
        else
            _mainForm.Close();
    }

    #endregion

    #region 窗体是否打开

    /// <summary>
    /// 窗体是否打开
    /// </summary>
    /// <param name="formType">窗体的Type</param>
    /// <returns>是否打开</returns>
    public static bool IsFormOpen(Type formType)
    {
        if (formType == null)
            return false;
        return _openForms.ContainsKey(formType);
    }

    /// <summary>
    /// 窗体是否打开
    /// </summary>
    /// <typeparam name="T">窗体的类型</typeparam>
    /// <returns>是否打开</returns>
    public static bool IsFormOpen<T>() where T : Form
    {
        Type formType = typeof(T);
        return _openForms.ContainsKey(formType);
    }

    #endregion

    #region 获取窗体对象

    /// <summary>
    /// 获取打开的 Form 窗体对象
    /// </summary>
    /// <typeparam name="T">窗体的类型</typeparam>
    /// <returns>窗体的实例</returns>
    public static T GetForm<T>() where T : Form
    {
        Type formType = typeof(T);
        if (_openForms.ContainsKey(formType))
            return (T)_openForms[formType];
        return null;
    }

    #endregion

    #region 切换界面

    /// <summary>
    /// 切换界面-1,针对无参数的构造函数
    /// </summary>
    /// <typeparam name="T">原窗体的类型</typeparam>
    /// <typeparam name="K">新窗体的类型</typeparam>
    public static void SwitchFrom<T, K>()
        where T : Form, new()
        where K : Form, new()
    {
        Type typeT = typeof(T);
        Type typeK = typeof(K);

        if (typeT == typeK)
        {
            AddErrorLog("[SwitchFrom1]参数 T 和 K 类型不能相同");
            return;
        }
        if (_openForms.ContainsKey(typeK))
        {
            AddErrorLog("[SwitchFrom1]要跳转的窗体已经打开");
            return;
        }

        OpenForm(typeK);
        CloseForm(typeT);
    }

    /// <summary>
    /// 切换界面-2,针对新窗体有参数的构造函数
    /// </summary>
    /// <typeparam name="T">原窗体的类型</typeparam>
    /// <typeparam name="K">新窗体的类型</typeparam>
    /// <param name="args">新窗体的构造函数参数</param>
    public static void SwitchFrom<T, K>(params object[] args)
        where T : Form, new()
        where K : Form
    {
        Type typeT = typeof(T);
        Type typeK = typeof(K);

        if (typeT == typeK)
        {
            AddErrorLog("[SwitchFrom2]参数 T 和 K 类型不能相同");
            return;
        }
        if (_openForms.ContainsKey(typeK))
        {
            AddErrorLog("[SwitchFrom2]要跳转的窗体已经打开");
            return;
        }

        if (!CheckOpenError(typeK, args))
            return;

        OpenForm(typeK, args);
        CloseForm(typeT);
    }

    #endregion

    #region 打开窗体

    /// <summary>
    /// 打开窗体-1
    /// </summary>
    /// <param name="formType">窗体的Type</param>
    public static void OpenForm(Type formType)
    {
        if (formType == null)
        {
            AddErrorLog("[OpenForm1]参数 formType 不能为空");
            return;
        }
        if (!formType.IsSubclassOf(typeof(Form)))
        {
            AddErrorLog("[OpenForm1]参数 formType 不是一个 Form 类型");
            return;
        }
        if (_mainForm == null)
        {
            AddErrorLog("[OpenForm1]字段 _mainForm 不能为空");
            return;
        }

        if (_mainForm.InvokeRequired)
            _mainForm.Invoke(new Action(() => ShowForm()));
        else
            ShowForm();

        void ShowForm()
        {
            if (!_openForms.ContainsKey(formType))
            {
                var form = (Form)Activator.CreateInstance(formType);
                form.FormClosed += (sender, e) => RemoveForm(formType);
                _openForms[formType] = form;
                form.Show();
            }
            else
            {
                Form existingForm = _openForms[formType];
                if (existingForm.WindowState == FormWindowState.Minimized)
                    existingForm.WindowState = FormWindowState.Normal;
                existingForm.BringToFront();
            }
        }
    }

    /// <summary>
    /// 打开窗体-2
    /// </summary>
    /// <param name="formType">窗体的Type</param>
    /// <param name="args">窗体的构造函数参数</param>
    public static void OpenForm(Type formType, params object[] args)
    {
        if (!CheckOpenError(formType, args))
            return;

        if (_mainForm == null)
        {
            AddErrorLog("[OpenForm2]字段 _mainForm 不能为空");
            return;
        }

        if (_mainForm.InvokeRequired)
            _mainForm.Invoke(new Action(() => ShowForm()));
        else
            ShowForm();

        void ShowForm()
        {
            if (!_openForms.ContainsKey(formType))
            {
                var form = (Form)Activator.CreateInstance(formType, args);
                form.FormClosed += (sender, e) => RemoveForm(formType);
                _openForms[formType] = form;
                form.Show();
                return;
            }
            else
            {
                Form existingForm = _openForms[formType];
                if (existingForm.WindowState == FormWindowState.Minimized)
                    existingForm.WindowState = FormWindowState.Normal;
                existingForm.BringToFront();
            }
        }
    }

    /// <summary>
    /// 打开窗体-3
    /// </summary>
    /// <typeparam name="T">窗体的类型</typeparam>
    /// <returns>窗体的实例</returns>
    public static T OpenForm<T>() where T : Form, new()
    {
        Type formType = typeof(T);

        if (_mainForm == null)
        {
            AddErrorLog("[OpenForm3]字段 _mainForm 不能为空");
            return null;
        }

        if (_mainForm.InvokeRequired)
            return (T)_mainForm.Invoke(new Func<T>(() => ShowForm()));
        else
            return ShowForm();

        T ShowForm() 
        {
            if (!_openForms.ContainsKey(formType))
            {
                T form = new T();
                form.FormClosed += (sender, e) => RemoveForm(formType);
                _openForms[formType] = form;
                form.Show();
                return form;
            }
            else
            {
                Form existingForm = _openForms[formType];
                if (existingForm.WindowState == FormWindowState.Minimized)
                    existingForm.WindowState = FormWindowState.Normal;
                existingForm.BringToFront();
                return (T)existingForm;
            }
        }
    }

    /// <summary>
    /// 打开窗体-4(有参数)
    /// </summary>
    /// <typeparam name="T">窗体的类型</typeparam>
    /// <param name="args">窗体的构造函数参数</param>
    /// <returns>窗体的实例</returns>
    public static T OpenForm<T>(params object[] args) where T : Form
    {
        if (args == null || args.Length == 0)
        {
            AddErrorLog("[OpenForm4]参数 args 不能为空");
            return null;
        }

        if (_mainForm == null)
        {
            AddErrorLog("[OpenForm4]字段 _mainForm 不能为空");
            return null;
        }

        Type formType = typeof(T);

        if (_mainForm.InvokeRequired)
            return (T)_mainForm.Invoke(new Func<T>(() => ShowForm()));
        else
            return ShowForm();

        T ShowForm()
        {
            if (!_openForms.ContainsKey(formType))
            {
                if (!CheckParametersConsistent(formType, args))
                {
                    AddErrorLog("[OpenForm4]没有找到 args 与之对应的构造函数");
                    return null;
                }

                T form = (T)Activator.CreateInstance(formType, args);
                form.FormClosed += (sender, e) => RemoveForm(formType);
                _openForms[formType] = form;
                form.Show();
                return form;
            }
            else
            {
                Form existingForm = _openForms[formType];
                if (existingForm.WindowState == FormWindowState.Minimized)
                    existingForm.WindowState = FormWindowState.Normal;
                existingForm.BringToFront();
                return (T)existingForm;
            }
        }
    }

    /// <summary>
    /// 窗体关闭时移除窗体
    /// </summary>
    /// <param name="formType">窗体的Type</param>
    private static void RemoveForm(Type formType)
    {
        _openForms.Remove(formType);
        if (_openForms.Count == 0)
        {
            CloseMainForm();
        }
    }

    /// <summary>
    /// 检查打开前是否有错误
    /// </summary>
    /// <param name="formType">窗体的Type</param>
    /// <param name="args">构造函数的参数</param>
    /// <returns></returns>
    private static bool CheckOpenError(Type formType, object[] args)
    {
        if (formType == null)
        {
            AddErrorLog("[CheckOpenError]参数 formType 不能为空");
            return false;
        }
        if (args == null || args.Length == 0)
        {
            AddErrorLog("[CheckOpenError]参数 args 不能为空");
            return false;
        }

        //已经打开的窗体,不必判断构造函数是否匹配
        if (!_openForms.ContainsKey(formType))
        {
            if (!CheckParametersConsistent(formType, args))
            {
                AddErrorLog("[CheckOpenError]没有找到 args 与之对应的构造函数");
                return false;
            }
        }
        return true;
    }

    /// <summary>
    /// 检查构造函数的参数和 args 是否匹配
    /// </summary>
    /// <param name="formType">窗体的Type</param>
    /// <param name="args">构造函数的参数</param>
    /// <returns></returns>
    private static bool CheckParametersConsistent(Type formType, object[] args)
    {
        var constructors = formType.GetConstructors();
        foreach (var constructor in constructors)
        {
            var parameters = constructor.GetParameters();
            if (parameters.Length != args.Length)
                continue;
            // 检查每个参数的类型是否匹配
            bool typesMatch = parameters
                .Select((t, i) => new { ParameterType = t.ParameterType, Arg = args[i] })
                .All(x => x.ParameterType.IsAssignableFrom(x.Arg.GetType()));
            if (typesMatch)
                return true;
        }
        return false;
    }

    #endregion

    #region 关闭窗体

    /// <summary>
    /// 关闭窗体-1
    /// </summary>
    /// <typeparam name="T">窗体的类型</typeparam>
    public static void CloseForm<T>() where T : Form
    {
        Type formType = typeof(T);

        if (_openForms.ContainsKey(formType))
        {
            if (_mainForm == null)
            {
                AddErrorLog("[CloseForm1]字段 _mainForm 不能为空");
                return;
            }

            if (_mainForm.InvokeRequired)
                _mainForm.Invoke(new Action(() => CloseForm()));
            else
                CloseForm();

            void CloseForm()
            {
                Form form = _openForms[formType];
                form.Close();
            }
        }
    }

    /// <summary>
    /// 关闭窗体-2
    /// </summary>
    /// <param name="formType">窗体的 Type</param>
    public static void CloseForm(Type formType)
    {
        if (_openForms.ContainsKey(formType))
        {
            if (_mainForm == null)
            {
                AddErrorLog("[CloseForm2]字段 _mainForm 不能为空");
                return;
            }

            if (_mainForm.InvokeRequired)
                _mainForm.Invoke(new Action(() => CloseForm()));
            else
                CloseForm();

            void CloseForm()
            {
                Form form = _openForms[formType];
                form.Close();
            }
        }
    }

    #endregion

    #region 其他

    //显示错误日志
    private static void AddErrorLog(string message)
    {
        if (IsShowErrorLog)
            Console.WriteLine(message);

        ErrorLogAction?.Invoke(message);
    }

    #endregion
}

那么当前的类库项目就完成了,接下来新建一个项目来测试这些功能

新建一个项目 Test,并添加三个窗体,项目结构如下:

Form1:

代码:

using System;
using System.Windows.Forms;

namespace Test1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            FormManager.IsShowErrorLog = true;
        }

        private void button1_Click(object sender, EventArgs e)
        {
            FormManager.SwitchFrom<Form1, Form2>();
            //FormManager.OpenForm<Form3>("1111");
        }
    }
}

Form2:

代码:

using System;
using System.Windows.Forms;

namespace Test1
{
    public partial class Form2 : Form
    {
        public Form2()
        {
            InitializeComponent();
        }

        private void Form2_Load(object sender, EventArgs e)
        {

        }

        private void button1_Click(object sender, EventArgs e)
        {
            FormManager.SwitchFrom<Form2, Form3>("这是Form2传入的参数");
        }
    }
}

Form3:

代码:

using System;
using System.Windows.Forms;

namespace Test1
{
    public partial class Form3 : Form
    {
        public Form3(string content)
        {
            InitializeComponent();
            this.Content = content;
        }

        private string Content = string.Empty;

        private void Form3_Load(object sender, EventArgs e)
        {
            label1.Text = Content;
        }
    }
}

Program 这里稍微做一下更改

using System;
using System.Windows.Forms;

namespace Test1
{
    internal static class Program
    {
        /// <summary>
        /// 应用程序的主入口点。
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(FormManager.MainFormControl<Form1>());
        }
    }
}

运行后的效果:

上面就是当前帖子所有的源码了,如果你需要 demo,也可以通过下面的链接下载:

https://download.youkuaiyun.com/download/qq_38693757/89720488

结束

如果这个帖子对你有所帮助,欢迎 关注 + 点赞 + 留言

end

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

熊思宇

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值