You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#中如何简洁实现子窗体多个按钮点击事件向父窗体传递

问题背景

在名为"Control Test"的子窗体上共布置18个按钮,点击事件需要向上传递给父窗体处理:其中14个为ON/OFF功能按钮,两两配对组成7组继电器控制项,子窗体界面如下:
子窗体界面
原有实现为每个按钮单独定义事件、触发方法、点击处理逻辑,子、父窗体都存在大量重复冗余代码,甚至出现复制粘贴导致的事件调用错误(如B Plus Relay OFF的触发方法误调用了ON按钮的事件委托),维护成本极高,可参考菜单事件的统一分发思路重构,具体实现如下。

实现方案

核心思路是所有按钮共用同一个点击入口,通过自定义事件参数传递操作标识,子窗体仅暴露一个统一事件,父窗体通过分支判断统一处理,和现有菜单的处理逻辑完全对齐。

1. 定义统一事件参数

首先定义操作类型枚举和自定义事件参数,用来传递当前点击对应的功能项、操作类型,替代原有每个事件单独传参的模式:

/// <summary>
/// 继电器操作类型
/// </summary>
public enum RelayActionType
{
    On,
    Off
}

/// <summary>
/// 继电器按钮点击事件统一参数
/// </summary>
public class RelayActionEventArgs : EventArgs
{
    /// <summary>
    /// 继电器功能唯一标识,如BPlus、BMinus、PreR等
    /// </summary>
    public string RelayKey { get; }
    /// <summary>
    /// 当前操作类型:开/关
    /// </summary>
    public RelayActionType ActionType { get; }

    public RelayActionEventArgs(string relayKey, RelayActionType actionType)
    {
        RelayKey = relayKey;
        ActionType = actionType;
    }
}

剩余4个非ON/OFF配对的按钮,可直接扩展枚举或在RelayKey中做区分,复用同一套事件逻辑。

2. 子窗体(CtrlTest)改造

  • 删掉原有每个按钮单独定义的事件、触发方法,仅保留一个统一的按钮点击事件
  • 设计器中给所有按钮设置Tag属性,按{功能标识}:{操作类型}格式存值,比如B+继电器ON按钮Tag设为BPlus:On,OFF按钮设为BPlus:Off
  • 所有按钮的Click事件统一绑定到同一个处理方法,解析Tag后触发统一事件:
// 仅需定义一个统一事件,无需为每个按钮单独声明事件
public event EventHandler<RelayActionEventArgs> RelayBtnClicked;

protected virtual void OnRelayBtnClicked(RelayActionEventArgs e)
{
    RelayBtnClicked?.Invoke(this, e);
}

/// <summary>
/// 所有继电器按钮共用的点击处理方法,设计器中直接为所有按钮选中该方法作为点击回调
/// </summary>
private void AllRelayBtn_Click(object sender, EventArgs e)
{
    if (sender is not Button btn || btn.Tag is not string tagContent) return;
    
    var tagParts = tagContent.Split(':');
    if (tagParts.Length != 2) return;
    if (!Enum.TryParse<RelayActionType>(tagParts[1], true, out var actionType)) return;

    // 触发统一事件,传递操作参数
    OnRelayBtnClicked(new RelayActionEventArgs(tagParts[0], actionType));
}

3. 父窗体改造

  • 删掉原有每个按钮事件单独绑定、单独写处理方法的逻辑,仅需为子窗体绑定一次统一事件
  • 事件处理方法中通过switch判断功能标识和操作类型,和现有菜单的处理逻辑完全一致:
case "controlTestToolStripMenuItem":
    if (Application.OpenForms["CtrlTest"] is CtrlTest ctrlTest)
    {
        ctrlTest.Focus();
        return;
    }
    ctrlTest = new CtrlTest();
    // 仅需绑定一次统一事件,无需写多行重复绑定代码
    ctrlTest.RelayBtnClicked += CtrlTest_RelayBtnClicked;
    ctrlTest.MdiParent = this;
    ctrlTest.Show();
    break;

统一事件处理逻辑:

private void CtrlTest_RelayBtnClicked(object sender, RelayActionEventArgs e)
{
    switch (e.RelayKey)
    {
        case "BPlus":
            if (e.ActionType == RelayActionType.On)
            {
                // 执行B+继电器开启逻辑
            }
            else
            {
                // 执行B+继电器关闭逻辑
            }
            break;
        case "BMinus":
            if (e.ActionType == RelayActionType.On)
            {
                // 执行B-继电器开启逻辑
            }
            else
            {
                // 执行B-继电器关闭逻辑
            }
            break;
        case "PreR":
            // 预充继电器逻辑
            break;
        // 其余功能项按相同规则添加分支即可
        default:
            break;
    }
}
方案收益
  • 重复代码量减少80%以上,从根源上避免复制粘贴导致的事件调用错误、命名错误
  • 后续新增按钮仅需在设计器配置Tag、绑定统一点击方法,父窗体加对应分支即可,无需新增事件定义、绑定、处理方法
  • 处理逻辑和现有菜单逻辑完全对齐,没有额外学习成本

内容的提问来源于stack exchange,提问作者Lha

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 04:15:30