|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.ComponentModel;
namespace System.Windows.Forms;
/// <summary>
/// ApplicationContext provides contextual information about an application
/// thread. Specifically this allows an application author to redefine what
/// circumstances cause a message loop to exit. By default the application
/// context listens to the close event on the mainForm, then exits the
/// thread's message loop.
/// </summary>
public class ApplicationContext : IDisposable
{
private Form? _mainForm;
/// <summary>
/// Creates a new ApplicationContext with no mainForm.
/// </summary>
public ApplicationContext() : this(null)
{
}
/// <summary>
/// Creates a new ApplicationContext with the specified mainForm.
/// If OnMainFormClosed is not overridden, the thread's message
/// loop will be terminated when mainForm is closed.
/// </summary>
public ApplicationContext(Form? mainForm)
{
// These are subclasses of the ApplicationContext for which we don't need to call the finalizer,
// because it's empty. See https://github.com/dotnet/winforms/issues/6858.
if (GetType() == typeof(ApplicationContext) || GetType() == Application.s_typeOfModalApplicationContext)
GC.SuppressFinalize(this);
MainForm = mainForm;
}
// NOTE: currently this finalizer is unneeded (empty). See https://github.com/dotnet/winforms/issues/6858.
// All classes that are not need to be finalized must be checked in ApplicationContext(Form? mainForm) constructor.
// Consider to modify it if needed.
~ApplicationContext() => Dispose(false);
/// <summary>
/// Determines the mainForm for this context. This may be changed
/// at anytime.
/// If OnMainFormClosed is not overridden, the thread's message
/// loop will be terminated when mainForm is closed.
/// </summary>
public Form? MainForm
{
get => _mainForm;
set
{
EventHandler onClose = OnMainFormDestroy;
if (_mainForm is not null)
{
_mainForm.HandleDestroyed -= onClose;
}
_mainForm = value;
if (_mainForm is not null)
{
_mainForm.HandleDestroyed += onClose;
}
}
}
[SRCategory(nameof(SR.CatData))]
[Localizable(false)]
[Bindable(true)]
[SRDescription(nameof(SR.ControlTagDescr))]
[DefaultValue(null)]
[TypeConverter(typeof(StringConverter))]
public object? Tag { get; set; }
/// <summary>
/// Is raised when the thread's message loop should be terminated.
/// This is raised by calling ExitThread.
/// </summary>
public event EventHandler? ThreadExit;
/// <summary>
/// Disposes the context. This should dispose the mainForm. This is
/// called immediately after the thread's message loop is terminated.
/// Application will dispose all forms on this thread by default.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (_mainForm is not null)
{
if (!_mainForm.IsDisposed)
{
_mainForm.Dispose();
}
_mainForm = null;
}
}
// If you are adding releasing unmanaged resources code here (disposing == false), you need to:
// 1. remove GC.SuppressFinalize from constructor of this class and from all of its subclasses
// 2. remove ApplicationContext_Subclasses_SuppressFinalizeCall test
// 3. modify ~ApplicationContext() description.
}
/// <summary>
/// Causes the thread's message loop to be terminated. This will call ExitThreadCore.
/// </summary>
public void ExitThread() => ExitThreadCore();
/// <summary>
/// Causes the thread's message loop to be terminated.
/// </summary>
protected virtual void ExitThreadCore() => ThreadExit?.Invoke(this, EventArgs.Empty);
/// <summary>
/// Called when the mainForm is closed. The default implementation
/// of this will call ExitThreadCore.
/// </summary>
protected virtual void OnMainFormClosed(object? sender, EventArgs e) => ExitThreadCore();
/// <summary>
/// Called when the mainForm is closed. The default implementation
/// of this will call ExitThreadCore.
/// </summary>
private void OnMainFormDestroy(object? sender, EventArgs e)
{
Debug.Assert(sender is Form);
Form form = (Form)sender;
if (!form.RecreatingHandle)
{
form.HandleDestroyed -= OnMainFormDestroy;
OnMainFormClosed(sender, e);
}
}
}
|