C# WinForms 多窗口交互通信的示例-主窗口子窗口交互通信

一、多窗口通信方式

C# WinForms 多窗口通信的方式有:

  • 构造函数传递
  • 属性传递
  • 接口
  • 事件通信
  • 委托回调
  • 静态消息中心

二、示例代码

C# WinForms中多窗口之间各种通信方式的示例。示例包含一个主窗口和多个子窗口,测试开发中常用的几种通信方式。

项目结构如下:

2.1 MessageCenter.cs 代码

csharp 复制代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MultiWindowCommunication.Message
{
    //class MessageCenter
    //{
    //}


    // 4. 使用静态类作为消息中心
    public static class MessageCenter
    {
        // 定义消息接收事件
        public static event Action<string, string> MessageReceived;

        // 发送消息的方法
        public static void SendMessage(string sender, string message)
        {
            // 触发所有订阅者的事件
            MessageReceived?.Invoke(sender, message);
        }
    }
}

2.2 ChildForm1.cs 代码

csharp 复制代码
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 MultiWindowCommunication.Forms
{
    // 定义接口用于子窗口向主窗口通信
    public interface IMainForm
    {
        void ReceiveMessageFromChild1(string message);
    }

    public partial class ChildForm1 : Form
    {
        public ChildForm1()
        {
            InitializeComponent();
        }

        // 2. 使用属性传递数据
        private string _receivedMessage;
        public string ReceivedMessage
        {
            get => _receivedMessage;
            set
            {
                _receivedMessage = value;
                txtReceived.Text = $"收到主窗口消息: {value}";
            }
        }

        // 保存主窗口引用
        private readonly IMainForm _mainForm;

        // 1. 使用构造函数传递数据
        public ChildForm1(string initialMessage, IMainForm mainForm)
        {
            InitializeComponent();
            _mainForm = mainForm;
            txtReceived.Text = initialMessage;
        }

        private void btnSendToMain_Click(object sender, EventArgs e)
        {
            if (!string.IsNullOrEmpty(txtMessage.Text) && _mainForm != null)
            {
                // 通过接口向主窗口发送消息
                _mainForm.ReceiveMessageFromChild1(txtMessage.Text);
                txtMessage.Clear();
            }
        }
    }
}

2.3 ChildForm2.cs 代码

csharp 复制代码
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 MultiWindowCommunication.Forms
{
    public partial class ChildForm2 : Form
    {
        // 定义事件用于向主窗口发送消息
        public event EventHandler<string> SendMessageToMain;
        public ChildForm2()
        {
            InitializeComponent();
        }

        // 供主窗口调用的方法,接收主窗口消息
        public void ReceiveMessage(string message)
        {
            txtReceived.Text = $"收到主窗口消息: {message}";
        }

        private void btnSendToMain_Click(object sender, EventArgs e)
        {
            if (!string.IsNullOrEmpty(txtMessage.Text))
            {
                // 触发事件,向主窗口发送消息
                SendMessageToMain?.Invoke(this, txtMessage.Text);
                txtMessage.Clear();
            }
        }
    }
}

2.4 ChildForm3.cs 代码

csharp 复制代码
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 MultiWindowCommunication.Forms
{
    public partial class ChildForm3 : Form
    {
        // 定义委托
        private Action<string> _callback;
        public ChildForm3()
        {
            InitializeComponent();
        }

        // 设置回调函数
        public void SetCallback(Action<string> callback)
        {
            _callback = callback;
        }

        private void btnSendToMain_Click(object sender, EventArgs e)
        {
            if (!string.IsNullOrEmpty(txtMessage.Text) && _callback != null)
            {
                // 通过委托向主窗口发送消息
                _callback(txtMessage.Text);
                txtMessage.Clear();
            }
        }
    }
}

2.5 ChildForm4.cs 代码

csharp 复制代码
using MultiWindowCommunication.Message;
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 MultiWindowCommunication.Forms
{
    public partial class ChildForm4 : Form
    {
        public ChildForm4()
        {
            InitializeComponent();
            // 订阅消息中心事件
            MessageCenter.MessageReceived += OnMessageReceived;
        }

        private void btnSendToAll_Click(object sender, EventArgs e)
        {
            if (!string.IsNullOrEmpty(txtMessage.Text))
            {
                // 通过消息中心发送消息
                MessageCenter.SendMessage("ChildForm4", txtMessage.Text);
                txtMessage.Clear();
            }
        }

        // 接收消息中心的消息
        private void OnMessageReceived(string sender, string message)
        {
            // 过滤掉自己发送的消息
            if (sender != "ChildForm4")
            {
                txtReceived.Text = $"从{sender}收到消息: {message}";
            }
        }

        private void ChildForm4_FormClosing(object sender, FormClosingEventArgs e)
        {
            // 取消订阅
            MessageCenter.MessageReceived -= OnMessageReceived;
        }
    }
}

2.6 MainForm.cs 代码

csharp 复制代码
using MultiWindowCommunication.Forms;
using MultiWindowCommunication.Message;
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 MultiWindowCommunication
{
    public partial class MainForm : Form, IMainForm
    {
        // 子窗口实例
        private ChildForm1 _childForm1;
        private ChildForm2 _childForm2;
        private ChildForm3 _childForm3;
        private ChildForm4 _childForm4;

        public MainForm()
        {
            InitializeComponent();
            // 初始化消息中心事件
            MessageCenter.MessageReceived += OnMessageFromMessageCenter;

        }

        #region 打开子窗口的方法
        private void btnOpenForm1_Click(object sender, EventArgs e)
        {
            if (_childForm1 == null || _childForm1.IsDisposed)
            {
                // 1. 使用构造函数传递数据
                _childForm1 = new ChildForm1("来自主窗口的初始消息", this);//(IMainForm)this
                _childForm1.Show();
            }
            else
            {
                _childForm1.BringToFront();
            }
        }

        private void btnOpenForm2_Click(object sender, EventArgs e)
        {
            if (_childForm2 == null || _childForm2.IsDisposed)
            {
                _childForm2 = new ChildForm2();
                // 2. 使用事件进行通信(子窗口到主窗口)
                _childForm2.SendMessageToMain += OnMessageFromChildForm2;
                _childForm2.Show();
            }
            else
            {
                _childForm2.BringToFront();
            }
        }

        private void btnOpenForm3_Click(object sender, EventArgs e)
        {
            if (_childForm3 == null || _childForm3.IsDisposed)
            {
                // 3. 使用委托进行通信(子窗口回调主窗口)
                _childForm3 = new ChildForm3();
                _childForm3.SetCallback(OnMessageFromChildForm3);
                _childForm3.Show();
            }
            else
            {
                _childForm3.BringToFront();
            }
        }

        private void btnOpenForm4_Click(object sender, EventArgs e)
        {
            if (_childForm4 == null || _childForm4.IsDisposed)
            {
                // 4. 使用静态类消息中心进行通信
                _childForm4 = new ChildForm4();
                _childForm4.Show();
            }
            else
            {
                _childForm4.BringToFront();
            }
        }
        #endregion

        #region 接收来自子窗口的消息
        // 处理来自ChildForm1的消息(通过接口)
        public void ReceiveMessageFromChild1(string message)
        {
            AddMessageToLog($"从ChildForm1收到: {message}");
        }

        // 处理来自ChildForm2的消息(通过事件)
        private void OnMessageFromChildForm2(object sender, string e)
        {
            AddMessageToLog($"从ChildForm2收到: {e}");
        }

        // 处理来自ChildForm3的消息(通过委托)
        private void OnMessageFromChildForm3(string message)
        {
            AddMessageToLog($"从ChildForm3收到: {message}");
        }

        // 处理来自消息中心的消息
        private void OnMessageFromMessageCenter(string sender, string message)
        {
            AddMessageToLog($"从{sender}通过消息中心收到: {message}");
        }
        #endregion

        #region 向子窗口发送消息
        private void btnSendToForm1_Click(object sender, EventArgs e)
        {
            if (_childForm1 != null && !_childForm1.IsDisposed)
            {
                // 使用属性传递数据(主窗口到子窗口)
                _childForm1.ReceivedMessage = txtMessage.Text;
            }
            else
            {
                MessageBox.Show("请先打开ChildForm1");
            }
        }

        private void btnSendToForm2_Click(object sender, EventArgs e)
        {
            if (_childForm2 != null && !_childForm2.IsDisposed)
            {
                // 直接调用子窗口方法
                _childForm2.ReceiveMessage(txtMessage.Text);
            }
            else
            {
                MessageBox.Show("请先打开ChildForm2");
            }
        }

        private void btnSendToALL_Click(object sender, EventArgs e)
        {
            // 通过消息中心向所有窗口广播消息
            MessageCenter.SendMessage("MainForm", txtMessage.Text);
        }
        #endregion

        // 添加消息到日志
        private void AddMessageToLog(string message)
        {
            txtLog.AppendText($"[{DateTime.Now:HH:mm:ss}] {message}{Environment.NewLine}");
            txtLog.ScrollToCaret();
        }



        private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            // 清理事件订阅
            MessageCenter.MessageReceived -= OnMessageFromMessageCenter;

            // 关闭所有子窗口
            if (_childForm1 != null && !_childForm1.IsDisposed)
                _childForm1.Close();

            if (_childForm2 != null && !_childForm2.IsDisposed)
            {
                _childForm2.SendMessageToMain -= OnMessageFromChildForm2;
                _childForm2.Close();
            }

            if (_childForm3 != null && !_childForm3.IsDisposed)
                _childForm3.Close();

            if (_childForm4 != null && !_childForm4.IsDisposed)
                _childForm4.Close();
        }
    }
}

2.7 通信方式详解

WinForms中的几种常用的多窗口通信方式:

  1. 构造函数传递(ChildForm1)

    • 适用于初始化时需要传递数据的场景
    • 优点:简单直接,适合初始化数据
    • 缺点:只能在创建窗口时传递一次
  2. 属性传递(ChildForm1)

    • 适用于需要多次传递数据的场景
    • 优点:可以在窗口生命周期内随时设置
    • 缺点:需要手动检查窗口是否已释放
  3. 事件通信(ChildForm2)

    • 适用于子窗口主动向父窗口发送消息
    • 优点:解耦性好,子窗口不需要知道父窗口具体类型
    • 缺点:需要手动管理事件订阅和取消订阅
  4. 委托回调(ChildForm3)

    • 适用于父窗口需要对子窗口消息做出响应的场景
    • 优点:灵活,可以传递复杂参数
    • 缺点:需要维护委托引用
  5. 静态消息中心(ChildForm4)

    • 适用于多个窗口之间需要互相通信的复杂场景
    • 优点:完全解耦,任意窗口间可通信
    • 缺点:需要管理事件订阅,可能导致内存泄漏

三、测试结果

四、建议

  • 简单的父子窗口通信,优先使用事件委托
  • 初始化数据传递,使用构造函数
  • 复杂的多窗口通信场景,使用静态消息中心
  • 无论使用哪种方式,都要注意在窗口关闭时清理事件订阅,避免内存泄漏

可以根据实际项目需求选择合适的通信方式,也可以结合多种方式使用。

相关推荐
专注VB编程开发20年3 小时前
C#,VB.NET数组去重复,提取键名和重复键和非重复键
c#·.net·linq·取唯一键·去重复·重复数量
YuanlongWang3 小时前
Entity Framework Core和SqlSugar的区别,详细介绍
c#
unicrom_深圳市由你创科技6 小时前
工业上位机,用Python+Qt还是C#+WPF?
python·qt·c#
偶尔的鼠标人17 小时前
Avalonia DataGrid 控件的LostFocus事件会多次触发
开发语言·c#
ytttr87317 小时前
C# 仿QQ聊天功能实现 (SQL Server数据库)
数据库·oracle·c#
future_studio19 小时前
聊聊 Unity(小白专享、C# 小程序 之 图片播放器)
unity·小程序·c#
梵得儿SHI1 天前
Prompt Engineering 核心知识:从基础模式到思维链,掌握大模型高效交互秘籍
大模型·prompt·交互·提示词·对话·大模型提问艺术·极简指令
c#上位机1 天前
wpf中Grid的MouseDown 事件无法触发的原因
c#·wpf
IT小哥哥呀1 天前
工业互联与设备IOT助力生产制造实践分享
物联网·交互·制造·简单工厂模式