最近有同事问道在应用程序启动之后,再次双击应用程序,如何保证不再启动新的应用程序,而是弹出之前已经启动的进程,本质上这就是创建一个单实例的WPF应用程序。在VS的工程树中有一个App.xaml和App.xaml.cs(这两个文件都是VS自动生成的),在App.xaml.cs中定义了App类,该类继承自System.Windows.Application,从类的命名上也很容易看出这些类是跟应用程序的管理相关的,在讲创建单实例应用程序之前,我们先了解一下Application这个类。

在上一篇WPF点滴(1) Main 函数中有部分对Application类的介绍,Application类封装了WPF应用程序,并管理其生命周期,以下代码用来创建一个Application:

Application app = new Application();
TestWindow window = new TestWindow();
app.MainWindow = window;
window.Show();
app.Run();

上面的代码创建了一个Application,并将该Application的主窗口设置为TestWindow,可以将以上代码简化为同样作用的以下代码:

Application app = new Application();
TestWindow window = new TestWindow();
app.Run(window);

需要注意的是VS自动生成的App类中使用了另一种方法来初始化Application和主窗口,通过资源路径来设置主窗口,

Application app = new Application();
app.StartupUri = new Uri("TestWindow.xaml", UriKind.Relative);
app.Run();

以上介绍了如何通过代码启动一个WPF应用程序,下面介绍以下Application类的几个常用场景:

1. 应用程序启动后的初始化和退出前的清理,这里会用到Startup和Exit这两个事件,或者重载OnStartup和OnExit,这两种方法是等效的,分别在程序启动和退出时触发;

2. 捕获应用程序未处理的异常,DispatcherUnhandledException,异常处理的原则是就近捕获,就近处理,这个事件更多是为了处理意料外的异常,防止程序奔溃的最后一层保护,因此不推荐过分依赖于这个事件中的异常处理。另外需要注意的是这个事件只会捕获主线程(UI线程)中的异常,后台线程中的未处理异常不会触发该事件;

3. 在后台线程中访问UI,Application.Current.Dispatcher.Invoke(),这种方法比较简单,另外一种方式是通过SynchronizationContext,这种方式更灵活一些。

现在终于可以切入正题了,如何创建创建单实例应用程序,前面说到了Startup这个事件,比较直接的方法是Startup的事件处理函数里判断是否已经存在了另外一个已经启动的应用程序,如果是就结束当前程序。这种方法可以实现单实例应用程序的效果,但是太暴力了,而且严格意义上并不是完全的单实例,应用程序毕竟已经启动了,虽然又被干掉了。

下面介绍的是WPF的推荐方式,使用Windows窗体提供的内置支持,借助WindowsFormsApplicationBase类,指定应用程序为单实例模式“IsSingleInstance = true”,

public class SingleInstanceApplication : WindowsFormsApplicationBase
{
  private App m_App;

  public SingleInstanceApplication()
  {
    IsSingleInstance = true;
  }

  protected override bool OnStartup(StartupEventArgs eventArgs)
  {
    m_App = new App
    {
      StartupUri = new System.Uri("MainWindow.xaml", System.UriKind.Relative)
    };
    m_App.Run();

    return false;
  }

  protected override void OnStartupNextInstance(StartupNextInstanceEventArgs eventArgs)
  {
    base.OnStartupNextInstance(eventArgs);
  }
}

重写Main函数(具体方法参见WPF点滴(1) Main 函数),可以看到SingleInstanceApplication充当了App的wrapper,通过SingleInstanceApplication来启动应用程序,并做单实例的的控制,SingleInstanceApplication.OnStartup()在应用程序首次启动时触发,SingleInstanceApplication.OnStartupNextInstance()在应用程序已经启动并尝试再次启动时触发。做这样的设计是为了实现类似Word这样的效果,Word只存在一个运行中的应用程序,每次双击并没有启动新的Word程序,只是打开了一个新窗口而已,而文档的路径就是通过“StartupEventArgs eventArgs”和“StartupNextInstanceEventArgs eventArgs”这两个命令行参数来传递的。

public class StartUp
{
  [STAThread]
  public static void Main(string[] args)
  {
    SingleInstanceApplication application = new SingleInstanceApplication();
    application.Run(args);
  }
}

WPF没有原生支持单实例模式,以上方法实际上是来自VB的一个设计特性,WindowsFormsApplicationBase的命名空间是Microsoft.VisualBasic.ApplicationServices,使用这个类需要添加对Microsoft.VisualBasic.dll的引用。

WPF点滴(2) 创建单实例应用程序的更多相关文章

  1. 使用 WPF 创建单实例应用程序

    一个简单的例子就是大家在使用很多应用程序,例如在使用Microsoft Word 时会遇到一种情况,不管你打开多少个文档,系统一次只能加载一个winword.exe 实例.当打开新文档时,文档在新窗口 ...

  2. WPF使用Mutex创建单实例程序失效

    vs2019 1.引入名称空间 using System.Threading; using System.Runtime.InteropServices; 2.导入dll并声明方法 [DllImpor ...

  3. WPF学习笔记 - 如何用WPF创建单实例应用程序

    使用一个已命名的(操作系统范围的)互斥量. bool mutexIsNew; using(System.Threading.Mutex m = new System.Threading.Mulex(t ...

  4. WPF 单实例应用程序

    例如:Microsoft Word,不管打开多少个文档(也不管它们是如何打开的),一次只能加载 winword.exe 一个实例. 这便是单实例应用程序. 对于这种单实例应用程序,WPF 本身并未提供 ...

  5. WPF:如何实现单实例的应用程序(Single Instance)

    原文:WPF:如何实现单实例的应用程序(Single Instance) 好吧,这是我将WPF与Windows Forms进行比较的系列文章的第四篇,讨论一下如何实现单实例(single instan ...

  6. Oracle - 给rac创建单实例dg,并做主从切换

    一.概述 本文将介绍如何给rac搭建单节点的dg,以及如何对其进行角色转换.预先具备的知识(rac搭建,单实例-单实例dg搭建) 二.实验环境介绍 主库rac(已安装rac,并已有数据库orcl)ra ...

  7. 关于struts和Spring 结合到一起之后存在ACtion创建单实例还是多

    struts 2的Action是多实例的并非单例,也就是每次请求产生一个Action的对象.原因是:struts 2的Action中包含数据,例如你在页面填写的数据就会包含在Action的成员变量里面 ...

  8. C# 实现单实例程序

    在我们经常使用的软件中,当我们已经打开后,再次打开时,有的软件不会出现两个.例如有道词典,会将上次的界面显示出来,或者提示我们“该程序已经运行...”.我通过一个简单的C# WPF例子来说明. 首先我 ...

  9. DevExpress Winform使用单例运行程序方法和非DevExpress使用Mutex实现程序单实例运行且运行则激活窗体的方法

    原文:DevExpress Winform使用单例运行程序方法和非DevExpress使用Mutex实现程序单实例运行且运行则激活窗体的方法 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA ...

随机推荐

  1. centos7装NVIDIA显卡驱动

    一.系统及显卡 系统:centos7.5 64位 显卡:gtx 1060 前几天主要是有一个人脸识别的项目测试,需要用到显卡去测试性能,然后装显卡的过程折腾了一下,特此记录. 二.安装过程 1. 下载 ...

  2. php下ajax的文件切割上传

    html5中的File对象继承Blob二进制对象,Blob提供了一个slice函数,可以用来切割文件数据. <!DOCTYPE HTML> <html lang="zh-C ...

  3. php libevent 详解与使用

    libevent是一个基于事件驱动的高性能网络库.支持多种 I/O 多路复用技术, epoll. poll. dev/poll. select 和 kqueue 等:支持 I/O,定时器和信号等事件: ...

  4. mockito使用

    mockito学习资料: http://docs.mockito.googlecode.com/hg/org/mockito/Mockito.html http://blog.csdn.net/sdy ...

  5. Spring框架之Bean的作用范围和生命周期的注解

    1. Bean的作用范围注解 * 注解为@Scope(value="prototype"),作用在类上.值如下: * singleton -- 单例,默认值 * prototype ...

  6. 如何利用jQuery post传递含特殊字符的数据【转】

    在jQuery中,我们通常利用$.ajax或$.post进行数据传递处理,但这里通常不能传递特殊字符,如:“<”.本文就介绍如何传递这种含特殊字符的数据. 1.准备页面和控制端代码 页面代码如下 ...

  7. php 框架选择

    背景 很多初级php甚至中级php都会陷入框架选择困难症,要么必须使用什么框架,要么一定不使用什么框架,而对框架的选择带来的效益和负担的成本并不是很清晰 框架大概分为以下这些 1. 简单轻量:tp,c ...

  8. python中的日志模块logging

    1.日志级别5个: 警告Warning 一般信息Info  调试 Debug  错误Error 致命Critical 2.禁用日志方法 logging.disable(logging.DEBUG) 3 ...

  9. tensorflow的transpose

    从图中看出来perm=[1,0,2] 表示第一个维度和第二个维度进行交换. 默认的是[0,1,2]   所以perm=[1,0,2] 表示第一个维度和第二个维度进行交换.0,1,2表示index.

  10. nlms_step_get

    module nlms_step_get(   rst ,   clk ,   nd ,      din01_i,   din01_q,   din02_i,   din02_q,      dou ...