Application 实现单一实例模式,以提供对其窗口、属性和资源范围服务的共享访问权限。 因此,每个 AppDomain(应用程序域)中仅且只有一个 Application 实例存在。
我们在写WPF的程序的时候,你根本没有创建Application与AppDomain的对象,但是你已经可以使用它们的对象了。
跳到App内部,可以看到如下代码(这段代码是VS自动生成的)。
这让我想到了MFC中的theApp,它管理了整个应用程序的流程。
继承关系:
Application 是封装 WPF 应用程序特定功能的类,包括:
-
应用程序生存期:Activated、、Current、、RunSessionEndingDeactivatedExitShutdownDispatcherUnhandledException、。 StartupShutdownMode
-
应用程序范围窗口、属性和资源管理:FindResource、、GetContentStream、、GetResourceStream、LoadComponent、MainWindowProperties、Resources、StartupUri。 Windows
-
命令行参数和退出代码处理:Application.Startup、、Application.ExitApplication.Shutdown。
-
导航:FragmentNavigation、、LoadCompleted、Navigated、NavigationProgressNavigating、NavigationStoppedNavigationFailed、、SetCookie。 GetCookie
AppDomain 像是Application的运行容器。一个进程可以创建多个AppDomain。AppDomain一般用于插件模式。这样主程序可以动态的加载可卸载其他AppDomain(插件),插件的崩溃也不会影响主程序。
下面是Application 和 AppDomain 常用的一些地方的小结:
Application 和 AppDomain 可用于,全局异常捕获!我们自定义的异常也是可以捕获到。
public App()
{
//UI线程异常
this.DispatcherUnhandledException += App_DispatcherUnhandledException;
//非UI线程异常
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
}
/// <summary>
/// UI线程异常
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
/// <exception cref="NotImplementedException"></exception>
private void App_DispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
{
e.Handled = true; // 消息不再继续往下流了(会流到,CurrentDomain_UnhandledException 非UI线程异常处理)
string msg = e.Exception.Message;
string func_name = e.Exception.TargetSite.Name;
string str_err = $"错误信息:{msg},| 调用方法:{func_name}";
logger.Error(str_err);
MessageBox.Show(str_err);
//throw new NotImplementedException();
}
/// <summary>
/// 非UI线程异常
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
/// <exception cref="NotImplementedException"></exception>
private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
logger.Error(e.ExceptionObject);
throw new NotImplementedException();
}
如果想在子线程里,更新界面信息,我们可以用到Application.Current.Dispatcher
在WPF中,所有的WPF对象都派生自DispatcherObject,DispatcherObject暴露了Dispatcher属性用来取得创建 对象线程对应的Dispatcher。DispatcherObject对象只能被创建它的线程所访问,其他线程修改 DispatcherObject需要取得对应的Dispatcher,调用Invoke或者BeginInvoke来投入任务。(Invoke和BeginInvoke等从WinForm时代就是一直存在的,WPF使用了Dispatcher来封装这些线程级的操作。原理和就是WINAPI中的PostMessage)
private void GuiHandler()
{
ushort CardID = 0;
while (LoopGui)
{
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
// 改变界面UI
// 相当于将更新消息投递到,界面处理消息队列
}));
Thread.Sleep(500);
}
}
在程序的任何地方调用System.Windows.Application.Current(Application类型),都能得到app对象,不过记得将其转换成我们自己的App的类型!