代码改变世界

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

2015-02-27 18:16  Dorisoy  阅读(1576)  评论(0编辑  收藏  举报

  一个简单的例子就是大家在使用很多应用程序,例如在使用Microsoft Word 时会遇到一种情况,不管你打开多少个文档,系统一次只能加载一个winword.exe 实例。当打开新文档时,文档在新窗口显示,但是始终只有一个应用程序控制所有文档窗口;如:可以提供平铺当前所有文档中相邻窗口的文档的特性。

  对于创建单实例的应用程序,WPF本身没有提供自带的解决方法,但可以通过变通的方式来实现——思路是当触发ApplicationStartup事件时,检查另一个实例是否在运行。方法是通过使用全局的mutex对像(mutex是操做系统中提供的用于进程间通信的同步方法)。虽然简单,但功能有限,如新实例无法与已经存在的实例进行通信。尤其是对于基于文档的用于程序而言,这确实是一个问题,如果新实例需要告诉已经存在的实例打开一个新的文档或者该文档是通过命令行参数传递的情况,那该使用什么方法来解决问题呢??

  WPF团队推荐我们一种最简单的方法就是:使用Windows  窗体提供的内置支持,该内置支持最初是用于VisualBasic 应用程序的,使用Window窗体和VisualBasic 的这一特新来开发基于C#的WPF程序会存在一个新旧应用程序之分,本质上旧式应用程序充当了WPF应用程序的封装器。流程是:当启动程序时将创建旧式应用程序,旧式应用程序接着创建WPF应用程序,旧式应用程序处理实例管理,而WPF应用程序处理正真的应用。

  下面我们通过一个实例来演示创建单实例应用程序的具体步骤:

 

    一.创建WPF窗体项目,并添加Microsoft.VisualBasic.dll 引用。

    

    二.首先创建一个Document.xaml,和 DocumentList.xaml 窗口文件,使用定义的类DocumentReference 表示对Document引用。

DocumentReference :

 1 public class DocumentReference
 2     {
 3         private Document document;
 4         public Document Document
 5         {
 6             get { return document; }
 7             set { document = value; }
 8         }
 9 
10         private string name;
11         public string Name
12         {
13             get { return name; }
14             set { name = value; }
15         }
16 
17         public DocumentReference(Document document, string name)
18         {
19             Document = document;
20             Name = name;
21         }
22     }
View Code

    Document.LoadFile() 通过文件名读取文档内容,Document.OnClosed() 事件在文档窗体关闭时触发,用于从动态集合移除实例。

 public partial class Document : Window
    {
        private DocumentReference docRef;

        public Document()
        {
            InitializeComponent();
        }

        public void LoadFile(DocumentReference docRef)
        {
            this.docRef = docRef;
            this.Content = File.ReadAllText(docRef.Name);
            this.Title = docRef.Name;
        }

        protected override void OnClosed(EventArgs e)
        {
            base.OnClosed(e);
            ((WpfApp)Application.Current).Documents.Remove(docRef);
        }

    }
View Code

  DocumentList 窗体放置一个 <ListBox Name="lstDocuments"></ListBox> 的 lstDocuments控件,用于显示当前文档列表。

 1 public partial class DocumentList : Window
 2     {
 3         public DocumentList()
 4         {
 5             InitializeComponent();
 6 
 7             lstDocuments.DisplayMemberPath = "Name";
 8             lstDocuments.ItemsSource = ((WpfApp)Application.Current).Documents;
 9         }       
10     }
View Code

    三.接着创建一个自定义的WPF类WpfApp.cs,该类继承于Windows.Application。在这里每当通过命令行参数传递给SingleInstanceApplication 类文件名时,会触发 SingleInstanceApplication 类的 OnStartupNextInstance 方法,并调用自定义的 ShowDocument() 方法为指定的文档加载文档窗体。

WpfApp:

 1  public class WpfApp : Application
 2     {
 3         private ObservableCollection<DocumentReference> documents =
 4             new ObservableCollection<DocumentReference>();
 5         public ObservableCollection<DocumentReference> Documents
 6         {
 7             get { return documents; }
 8             set { documents = value; }
 9         }        
10 
11         protected override void OnStartup(System.Windows.StartupEventArgs e)
12         {
13             base.OnStartup(e);
14             //WpfApp.Current = this;
15 
16             DocumentList list = new DocumentList();
17             this.MainWindow = list;
18             list.Show();
19 
20             // Load the document that was specified as an argument.
21             if (e.Args.Length > 0) ShowDocument(e.Args[0]);
22 
23         }
24 
25         public void ShowDocument(string fileName)
26         {
27             try
28             {
29                 Document doc = new Document();
30                 DocumentReference docRef = new DocumentReference(doc, fileName);
31                 doc.LoadFile(docRef);
32                 doc.Owner = this.MainWindow;
33                 doc.Show();
34                 doc.Activate();
35                 Documents.Add(docRef);
36             }
37             catch
38             {
39                 MessageBox.Show("Could not load document.");
40             }
41         }
42     }
View Code

  四.创建继承于WindowsFormsApplicationBase 的自定义类 SingleInstanceApplication.cs 该类将提供3个用于管理实例的重要成员。

  • IsSingleInstance 用于确定此应用程序是否为单实例应用程序,在构造函数中设置值为true。
  • 重写 OnStartup(),程序启动时,重写该方法并创建WPF应用程序对象。
  • 重写 OnStartupNextInstance(),当另一个应用程序启动时触发该方法,该方法提供了访问命令行的参数的功能。此时可以调用WPF应用程序类的方法来创建窗口,但不能创建另一个应用程序对象。

     SingleInstanceApplication:

 1 public class SingleInstanceApplication : WindowsFormsApplicationBase
 2     {
 3         private WpfApp App;
 4         public SingleInstanceApplication()
 5         {
 6             this.IsSingleInstance = true;
 7         }
 8 
 9         protected override bool OnStartup(Microsoft.VisualBasic.ApplicationServices.StartupEventArgs eventArgs)
10         {
11             string extension = ".testDoc";
12             string title = "SingleInstanceApplication";
13             string extensionDescription = "A Test Document";
14 
15             //return base.OnStartup(eventArgs);
16             App = new WpfApp();
17             App.Run();
18             return false;
19         }
20 
21         protected override void OnStartupNextInstance(StartupNextInstanceEventArgs eventArgs)
22         {
23             //base.OnStartupNextInstance(eventArgs);
24             if (eventArgs.CommandLine.Count > 0)
25                 App.ShowDocument(eventArgs.CommandLine[0]);
26         }
27     }
View Code

   五. 由于应用程序需要在APP类之前就要创建SingleInstanceApplication 类,所以这里就需要同过传统的Main方法作为启动入口。

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

 六,为了测试单例程序,需要使用Window文件扩展名与程序相关联,将其文件类型注册。运行结果如下: