关闭所有子窗体时退出应用程序
Exit application when all child forms are closed
在我的 winform 应用程序中,我有一个表单在 for 循环期间创建其他表单。此父表单保持隐藏状态,纯粹用于处理。
我将处理代码从程序中移开 class 因为 Application.Run
似乎不适合在循环中放置 nice,因为会打开一个以上的实例。
子窗体完成后关闭。我想知道的是,即使父窗体仍处于打开状态,当这些窗体关闭时,我是否可以让应用程序退出。我尝试在父表单上公开一个 List<bool>
以存储哪些表单已关闭,但由于父表单没有实例名称,子表单无法访问列表 - 它由 Application.Run(new FormProcessor(args));
调用
更一般地说,我想我问的是有没有一种方法可以从子窗体访问父窗体的属性。
在每个子窗体的构造函数中注入对父窗体的引用,从而允许对其进行访问。
或者在创建每个子窗体时添加对父窗体列表的引用,然后 运行 后台任务等待所有子窗体关闭。您可以通过在每个子表单上订阅表单关闭事件并等待它们触发来做到这一点
我试图在评论中展示的一个简单示例如下。
您创建一个额外的 class 来处理 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();
}
}
}
当然,在我的示例中它们只是基本的 windows,我不关心任何线程或其他 GUI 细节,但它应该向您展示您可以使用事件注册做什么的基础知识以及 ObservableCollection<T>
的用法(参见 FormSubscriber)
最简单的方法是将有关 opened/closed 表单的信息保存在其他一些全局 class 中,例如:
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();
}
使用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();
}
}
}
感谢所有为此提供帮助的人。我想出了一个对我有用的答案。我在关闭的表单后面添加了一个事件处理程序。 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(); }
}
在我的 winform 应用程序中,我有一个表单在 for 循环期间创建其他表单。此父表单保持隐藏状态,纯粹用于处理。
我将处理代码从程序中移开 class 因为 Application.Run
似乎不适合在循环中放置 nice,因为会打开一个以上的实例。
子窗体完成后关闭。我想知道的是,即使父窗体仍处于打开状态,当这些窗体关闭时,我是否可以让应用程序退出。我尝试在父表单上公开一个 List<bool>
以存储哪些表单已关闭,但由于父表单没有实例名称,子表单无法访问列表 - 它由 Application.Run(new FormProcessor(args));
调用
更一般地说,我想我问的是有没有一种方法可以从子窗体访问父窗体的属性。
在每个子窗体的构造函数中注入对父窗体的引用,从而允许对其进行访问。
或者在创建每个子窗体时添加对父窗体列表的引用,然后 运行 后台任务等待所有子窗体关闭。您可以通过在每个子表单上订阅表单关闭事件并等待它们触发来做到这一点
我试图在评论中展示的一个简单示例如下。
您创建一个额外的 class 来处理 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();
}
}
}
当然,在我的示例中它们只是基本的 windows,我不关心任何线程或其他 GUI 细节,但它应该向您展示您可以使用事件注册做什么的基础知识以及 ObservableCollection<T>
的用法(参见 FormSubscriber)
最简单的方法是将有关 opened/closed 表单的信息保存在其他一些全局 class 中,例如:
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();
}
使用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();
}
}
}
感谢所有为此提供帮助的人。我想出了一个对我有用的答案。我在关闭的表单后面添加了一个事件处理程序。 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(); }
}