【问题标题】:Exit application when all child forms are closed关闭所有子表单时退出应用程序
【发布时间】:2015-09-21 13:28:59
【问题描述】:

在我的 winform 应用程序中,我有一个表单可以在 for 循环期间创建其他表单。这个父表单仍然隐藏,纯粹是为了处理。

我将处理代码从程序类中移开,因为Application.Run 似乎没有很好地放在一个循环中,因为会打开多个实例。

当子窗体完成后,它们将关闭。我想知道的是,即使父表单仍然打开,当这些表单关闭时,我是否可以让应用程序退出。我尝试在父表单上公开List<bool> 以存储哪些表单已关闭,但由于父表单没有实例名称,子表单无法访问列表 - 它由Application.Run(new FormProcessor(args));调用

更一般地说,我想我问的是有没有一种方法可以从子表单访问父表单的属性。

【问题讨论】:

  • 您可以使用共享视图模型,该模型从父表单中初始化,并简单地作为构造函数参数或属性提供给子表单。另一方面,所有子表单都应该有一个 FormClosed 事件,您可以将自己绑定到该事件,当表单关闭时,您从列表中删除对该表单的引用,一旦您有一个空列表,您调用 @ 987654326@
  • 这一切似乎是替换ApplicationContext的不必要的黑客攻击

标签: c# forms winforms parent-child exit


【解决方案1】:

在每个子表单的构造函数中注入对父表单的引用,从而允许访问它。

或者在创建每个子表单时,在父表单中添加对列表的引用,然后运行后台任务以等待所有子表单关闭。您可以通过订阅每个子表单上的表单关闭事件并等待它们触发来做到这一点

【讨论】:

    【解决方案2】:

    我试图在 cmets 中展示的一个简单示例如下。

    您创建一个额外的类来处理 FormClosed 事件的注册,例如:

    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Collections.Specialized;
    using System.ComponentModel;
    using System.Windows.Forms;
    
    namespace wfAutoClose {
      public class FormSubscriber : INotifyPropertyChanged, IDisposable {
        public event PropertyChangedEventHandler PropertyChanged;
    
        private readonly IList<Form> _forms = new ObservableCollection<Form>();
        public IList<Form> Forms {
          get {
            return _forms;
          }
        }
    
        public int FormCount {
          get {
            return _forms.Count;
          }
        }
    
        private void RaisePropertyChanged(string propertyName) {
          var localEvent = PropertyChanged;
          if (localEvent != null) {
            localEvent.Invoke( this, new PropertyChangedEventArgs( propertyName: propertyName ) );
          }
        }
    
        private void OnChildFormClosed(object sender, FormClosedEventArgs e) {
          Forms.Remove( sender as Form );
        }
    
        private void SubscribeToFormClosedEvent(Form childForm) {
          childForm.FormClosed += OnChildFormClosed;
        }
    
        private void UnsubscribeFromFormClosedEvent(Form childForm) {
          childForm.FormClosed -= OnChildFormClosed;
        }
    
        private void OnChildFormCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) {
          if (e.OldItems != null) {
            foreach (var item in e.OldItems) {
              UnsubscribeFromFormClosedEvent( item as Form );
            }
          }
          if (e.NewItems != null) {
            foreach (var item in e.NewItems) {
              SubscribeToFormClosedEvent( item as Form );
            }
          }
          RaisePropertyChanged( "FormCount" );
        }
    
        public void Dispose() {
          ( Forms as INotifyCollectionChanged ).CollectionChanged -= OnChildFormCollectionChanged;
        }
    
        public FormSubscriber() {
          ( Forms as INotifyCollectionChanged ).CollectionChanged += OnChildFormCollectionChanged;
        }
      }
    }
    

    然后可以在您的父表单中使用此表单,您只需在循环中添加表单,您的父表单可以将自己注册到 FormSubscriber 的 INotifyPropertyChanged 事件。当没有更多可用的表单时,它将显示FormCount == 0,这是您将从应用程序中退出的位置(通过调用Application.Exit()this.Close()

    using System;
    using System.ComponentModel;
    using System.Windows.Forms;
    
    namespace wfAutoClose {
      public partial class Form1: Form {
        FormSubscriber formSubscriber = new FormSubscriber();
    
        public Form1() {
          InitializeComponent();
          formSubscriber.PropertyChanged += OnPropertyChanged;
        }
    
        private void OnPropertyChanged( object sender, PropertyChangedEventArgs e ) {
          if (formSubscriber.FormCount == 0) {
            Application.Exit();
          }
        }
    
        private void Form1_Load( object sender, EventArgs e ) {
          for ( int i = 0; i < 3; i++ ) {
            Form form = new Form2() { Text = "Dynamic Form " + i };
            form.Show();
            formSubscriber.Forms.Add( form );
          }
        }
    
        private void Form1_FormClosed( object sender, FormClosedEventArgs e ) {
          formSubscriber.Dispose();
        }
      }
    }
    

    当然,在我的示例中,它们只是基本窗口,我不关心任何线程或其他 GUI 细节,但它应该向您展示您可以通过事件注册和 @ 的用法进行的基本操作987654326@(见表单订阅者)

    【讨论】:

    • 感谢@icepickle,我采用了类似的方法。 Form1 frm = new Form1(formname...); frm.Show(); frm.FormClosed += new formClosedEventHandler((sender, e) =&gt; FormClosed_Event(sender, e, formname));
    【解决方案3】:

    使用 ApplicationContext 并订阅所有子窗体的 FormClosed() 事件。在适当的时候检查 Application.OpenForms 集合和 Exit()...

    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new MyContext());
        }
    
    }
    
    public class MyContext : ApplicationContext
    {
    
        public MyContext()
        {
            // Open your Forms...
            for(int i = 1; i <= 5; i++)
            {
                Form frm = new Form();
                frm.Text = "Form #" + i.ToString();
                frm.FormClosed += Frm_FormClosed;
                frm.Show(); 
            }
        }
    
        private void Frm_FormClosed(object sender, FormClosedEventArgs e)
        {
            if (Application.OpenForms.Count == 0)
            {
                Application.Exit();
            }
        }
    
    }
    

    【讨论】:

      【解决方案4】:

      最简单的方法是将有关打开/关闭表单的信息保存在其他一些全局类中,例如:

      public static class Helper
      {
         public static List<int> ChildFormsOpened { get; private set; }
      
         static Helper()
         {
             ChildFormsOpened = new List<int>();   
         }
      }
      

      您可以在打开它时简单地添加表单哈希码。您可以在子窗体打开时在 ctor 或加载事件处理程序中执行此操作:

      Helper.ChildFormsOpened.Add(this.GetHashCode());
      

      因此,在您的代码中的某个时刻,您可以从集合中删除正在关闭的表单,并检查所有其他表单是否也已关闭。如果是,那么您可以通过调用 Application.Exit() 方法来关闭您的应用程序:

          private void Form1_FormClosing(object sender, FormClosingEventArgs e)
          {
             Helper.ChildFormsOpened.Remove(this.GetHashCode());
             if(Helper.ChildFormsOpened.Count < 1) Application.Exit();
          }    
      

      【讨论】:

      • 哪个组件需要检查ChildFormOpened列表是否为0?关闭它的那个人,或者你的样本中的父母,不知道有什么东西被关闭了?
      • 嗯,这取决于项目需要 OP 将在哪里进行此检查,从哪个类。但我想在 childFormClosing 事件处理程序中检查它是最简单的选择。当每个子表单即将关闭时,它会进行此检查,并在必要时(所有表单都已关闭) - 关闭应用程序
      【解决方案5】:

      感谢所有为此提供帮助的人。我想出了一个适合我的答案。我在关闭表单的背面添加了一个事件处理程序。 origList 变量存储 formList 的原始数据,否则 foreach 将继续到它可能已删除的列表的下一个条目。

      for ( int i =0; i < formList.Count; i++)
              {
                      string formName = formList[i];
      
                          Form1 frm = (new Form1(formName...);
                          frm.Show();
                          string contextName= formName;
                          frm.FormClosed += new FormClosedEventHandler((sender, e) => FrmClosed_Event(sender, e, contextName));                                     
              }
      
             public void FrmClosed_Event(object sender, FormClosedEventArgs e, string name)
              {
                  foreach(string thisForm in origList)
                  {
                      if (thisForm == name)
                      { formList.Remove(thisForm); }
                  }
                  if (formList.Count == 0)
                  { Application.Exit(); }
              }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-03-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多