在我的 winform 应用程序中,我有一个表单在 for 循环期间创建其他表单。该父表单保持隐藏状态,纯粹用于处理。
我将处理代码从程序类中移开,因为
Application.Run
似乎不太适合放在循环中,因为会打开多个实例。
当子表单完成后,它们就会关闭。我想知道的是,当这些表单关闭时,即使父表单仍然打开,我是否可以让应用程序退出。我尝试在父表单上公开
List<bool>
来存储哪些表单已关闭,但由于父表单没有实例名称,子表单无法访问该列表 - 它由 Application.Run(new FormProcessor(args));
调用
更一般地说,我想我问是否有一种方法可以从子表单访问父表单的属性。
在每个子表单的构造函数中注入对父表单的引用,从而允许对其进行访问。
或者当创建每个子表单时,添加对父表单中的列表的引用,然后运行后台任务以等待所有子表单关闭。 您可以通过订阅每个子表单上的表单关闭事件并等待它们触发来完成此操作
我试图在评论中展示的一个简单示例如下。
您创建一个额外的类来处理 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 细节,但它应该向您展示事件注册和使用
ObservableCollection<T>
可以做什么的基础知识(参见表格订阅者)
使用ApplicationContext并订阅所有子表单的FormClosed()事件。 检查 Application.OpenForms 集合并在适当时退出()...
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();
}
}
}
最简单的是将有关打开/关闭表单的信息保存在其他一些全局类中,例如:
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();
}
感谢所有为此提供帮助的人。我想出了一个对我有用的答案。我在关闭的表单背面添加了一个事件处理程序。
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(); }
}
使用
ApplicationContext
,每当当前 OpenForms
关闭并退出(如果没有更多 MainForm
)时,它会选择其中一个 MainForm
作为新的 OpenForms
。
internal sealed class MyApplicationContext : ApplicationContext
{
public MyApplicationContext(Form mainForm)
: base(mainForm)
{
}
protected override void OnMainFormClosed(object? sender, EventArgs e)
{
if (Application.OpenForms.Count > 0)
{
MainForm = Application.OpenForms[0];
return;
}
base.OnMainFormClosed(sender, e);
}
}