【问题标题】:Pass variable between forms when clicking button单击按钮时在表单之间传递变量
【发布时间】:2026-01-04 06:30:01
【问题描述】:

我有两个表格。一个与所有主要代码正在执行的地方。使用此方法单击菜单项时会显示另一种形式:

Form2 videoSettings = new Form2();       

private void videoToolStripMenuItem_Click(object sender, EventArgs e)
{
    videoSettings.Show();
}

随后打开的表单包含用户可以为应用程序设置一些设置的字段。 然后单击“保存”按钮时,我想要这个变量:public int deviceIndex; 从原始表格中获取。

所以我想知道是否可以在 Form1 中添加任何事件或其他内容,以检测在 videoSettings (Form2) 中单击保存按钮的时间?

【问题讨论】:

    标签: c# .net winforms parameter-passing


    【解决方案1】:

    我会以不同的方式来做。我会将 UI 处理和业务逻辑层之间的代码分开。所以你的场景将以这样的方式运行:

    1. 第一个表单发出一个事件,通知具有特定语义的按钮已被激活。处理所需的数据包含在事件数据中。
    2. 业务逻辑监听此事件,并决定在第二个表单上发出命令。它在表单的类上调用适当的方法,将所需的信息作为参数传递(如果需要,还可以对参数进行预处理)。
    3. 第二种形式接收来自业务逻辑的命令并更新视图。

    这样问题就不会出现了。


    示例:(我不是 winforms 专家,请注意,从最佳 winforms 实践的 POV 来看,这可能是完全错误的。)

    第 1 部分(第一种形式):

    class ProcessingActivatedEventArgs : EventArgs
    {
        public ProcessingActivatedEventArgs(int data) { MoreData = data; }
        public int MoreData { get; protected set; }
    }
    
    class Form1 : Form
    {
        private int currentData;
        public event EventHandler<ProcessingActivatedEventArgs> ProcessingActivated;
        void OnButtonClick(object sender, EventArgs args)
        {
            // ...
            if (ProcessingActivated != null)
                ProcessingActivated(new ProcessingActivatedEventArgs(currentData));
        }
    }
    

    第 2 部分:(业务逻辑)

    class Controller
    {
        Form1 f1;
        Form2 f2;
    
        void StartFirstForm()
        {
            f1 = new Form1();
            f1.ProcessingActivated += OnProcessingActivated;
            f1.Show();
        }
    
        void OnProcessingActivated(object sender, ProcessingActivatedEventArgs args)
        {
            int data = args.MoreData;
            f1.DisableProcessingRequests();
            model.ProcessingFinished += OnProcessingFinished;
            model.StartProcessing(data);
            if (data > 0)
                f2.DisplayDataProcessing(0, data);
            else if (data < 0)
                f2.DisplayDataProcessing(data, 0);
            else
                throw new SomeCoolException("impossible data");
        }
    }
    

    第 3 部分:(第二种形式)

    class Form2 : Form
    {
        public void DisplayDataProcessing(int lower, int upper)
        {
            // ... update the UI
        }
    }
    

    请注意,此实现与Controller 联系在一起,并且形式比它所能做的更紧密。在WPF中,解耦是通过使用适当的DataContext来实现的(但我不知道如何在WinForms中正确地做到这一点)。

    【讨论】:

    • 这听起来确实是一个更好的主意!你有任何使用这个版本的例子吗?或者你可以链接到熟悉的东西,这样我就可以掌握它。
    • @Robert:我不是winforms专家,所以我不知道表达视图和业务逻辑之间关系的惯用方式是什么。不过我会试试的。
    • @Vlad:完成一个非常简单的任务似乎过于复杂。
    • @caesay:职责分离使得应用的整体结构更简单,逻辑更清晰。真的值得多上一堂课。您的维护者将感激您。或者,应用程序可以在OnClicks 中执行所有操作。
    • @caesay:如果您习惯了将事件和代码直接放入表单的常用方式,这最初可能看起来很复杂,但随着应用程序变得越来越大,它会更好地扩展,并且有很多变成大泥球的几率更低。
    【解决方案2】:

    有很多建议,但我想加两分钱。

    你可以使用 form2.ShowDialog();这将停止执行您的 form1 线程,直到 form2 关闭。这意味着您可以从 form1 执行此操作:

    Form2 videoSettings = new Form2();  
    
    //show options
    videoSettings.ShowDialog();
    
    //at this point, the user has either clicked save, cancel, or closed the form 
    //(because the form is closed, obviously :) )
    int device = videoSettings.deviceIndex;
    

    如果你不能让它像那样锁定你的表单,这里是另一种在 Form2 中使用事件的方法:

    Form2 : Form
    {
        public event EventHandler Saved;
    
        OnSaveButtonClicked(...)
        {
            if(Saved != null) Saved(this, new EventArgs());
        }
    }
    

    然后从Form1:

    Form2 frm = new Form2();
    frm.Saved += (s, e) =>
    {
        //saved button clicked, retrieve value.
        //also could be handled as a method, or really any way.
    };
    frm.Show();
    

    【讨论】:

      【解决方案3】:

      让我建议另一种方式,介于最简单的 ShowDialog() 和精细的业务逻辑和接口分离方式之间。

      我希望在 Form2 中创建一个新事件。我将此事件称为SettingsSaved

      在 Form2 中添加为全局声明

      public delegate void SettingsSavedEventHandler(object sender, SettingsSavedEventArgs e);
      public event SettingsSavedEventHandler SettingsSaved;
      

      在 cmdSave_Click 事件中

      if(SettingsSaved != null)
      {
          SettingsSavedEventArgs ss = new SettingsSavedEventArgs() { DeviceIndex = deviceIndex};
          SettingsSaved(this, ss); 
      }
      

      SettingsSavedEventArgs 类的骨架

      public class SettingsSavedEventArgs: EventArgs
      {
          public int DeviceIndex {get; set;}  
          // Other settings could be added here
      }
      

      现在在调用 Form2 的代码中,我们可以订阅事件并在用户单击 Form2 保存按钮时收到通知

      Form2 videoSettings = new Form2();
      videoSettings.SettingsSaved += new SettingsSavedEventHandler(SavedHandler);
      videoSettings.Show();     
      ....
      
      private void SavedHandler(object sender, SettingsSavedEventArgs ss)
      {
          int deviceIndex = ss.DeviceIndex;
      }
      

      Observer Pattern

      【讨论】:

      • 这就像我的第二个例子,除了更多的代码:)
      【解决方案4】:

      你可以像这样传递信息

      private Form1 mainForm = null;
      public Form2(Form callingForm)
      {
          mainForm = callingForm as Form1; 
          InitializeComponent();
      }
      

      然后,您可以像这样从 Form2 访问 Form1 属性:

      //Call this in Save button click event 
      this.mainForm.deviceIndex;
      

      【讨论】:

      • 他只需要那个属性,所以为什么要进行所有的表格。如果我是你,我会使用接口。
      • 可以有替代方案,Interface可以提供更好的解决方案。我只是尽我所能。
      【解决方案5】:

      也许您可以尝试让您的第二种形式来实现INotifyPropertyChanged 接口。然后,当您单击保存时,您会引发 PropertyChanged 事件,并在第一个表单中捕获它。

      【讨论】: