WPF:Application Management应用程序管理(1)

CustomApplication自定义程序

效果:

  1. 引用另一个项目中的一个类,本项目继承它并开始启动。

clipboard.png
实现思路:

  1. 项目B中的类继承入口Application并重写启动函数,其中添加功能。
  2. 本项目A引用个项目,继承了CustomApplication入口功能及自定义功能,自然就重用了自定义添加功能。
  3. 注意本项目文件App.xaml的启动文件为StartupUri="MainWindow.xaml",MainWindow.xaml其下定义类名为App:CustomAppliction,引用另一个项目文件CustomApplictionClient下的类名CustomAppliction:Application
  4. 本项目App.xaml文件中自定义开头使用了项目B的CustomApplication类代替,注意命名空间及partial类名App的同样继承类:
<library:CustomApplication x:Class="CustomApplicationClass.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:library="clr-namespace:CustomApplicationClient;assembly=CustomApplicationClient"
             StartupUri="MainWindow.xaml"/>
namespace CustomApplicationClient
{
    public class CustomApplication : Application
    {
        protected override void OnStartup(StartupEventArgs e)
        {
            base.OnStartup(e);
            MessageBox.Show(@"Hello, reusable custom application!");
        }
    }
}
namespace CustomApplicationClass
{
    /// <summary>
    ///     Interaction logic for App.xaml
    /// </summary>
    public partial class App : CustomApplication
    {
    }
}

ApplicationShutdown应用程序关闭

clipboard.png

实现:

  1. 主窗口关闭事件怎么影响子窗口
  2. 取消或通知关闭事件,或进行其他处理。

扩展:

  1. ShutdownMode 设置为 OnLastWindowClose,则 WPF 会在应用程序中的最后一个窗口关闭时隐式调用 Shutdown,即使任何当前已经实例化的窗口被设置为主窗口也是如此。默认值。
  2. 设置为 OnMainWindowClose 的 ShutdownMode 会导致WPF在 MainWindow 关闭时隐式调用 Shutdown,即使其他窗口当前仍打开。ps,关闭事件会传递各窗口而最终都会关闭窗口,不管是否有退出对话框取消事件。
  3. 某些应用程序的生存期可能并不取决于主窗口或最后一个窗口的关闭时间,也可能根本不取决于窗口。 对于这些情况,需要将 ShutdownMode 属性设置为 OnExplicitShutdown,这需要显式的 Shutdown 方法调用来停止应用程序。 否则,应用程序会继续在后台运行。
  4. 此属性只能从创建 Application 对象的线程获得。
  5. Shutdown(Int32) 关闭将指定退出代码返回给操作系统的应用程序。 ??
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
    shutdownModeListBox.Items.Add("OnLastWindowClose");
    shutdownModeListBox.Items.Add("OnExplicitShutdown");
    shutdownModeListBox.Items.Add("OnMainWindowClose");
    shutdownModeListBox.SelectedValue = "OnLastWindowClose";
    shutdownModeListBox.SelectionChanged +=
        shutdownModeListBox_SelectionChanged;
    Application.Current.ShutdownMode = ShutdownMode.OnLastWindowClose;
}

private void shutdownModeListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    Application.Current.ShutdownMode =
        (ShutdownMode) Enum.Parse(typeof (ShutdownMode), shutdownModeListBox.SelectedValue.ToString());
}

private void newWindowButton_Click(object sender, RoutedEventArgs e)
{
    (new ChildWindow()).Show();
}

private void explicitShutdownButton_Click(object sender, RoutedEventArgs e)
{
    var exitCode = 0;
    int.TryParse(appExitCodeTextBox.Text, out exitCode);
    Application.Current.Shutdown(exitCode);
}

子窗口退出对话框,在xaml设置closing/closed事件

private void ChildWindow_Closing(object sender, CancelEventArgs e)
{
    Console.WriteLine(@"Closing");
    var result = MessageBox.Show("Allow Shutdown?", "Application Shutdown Sample",
        MessageBoxButton.YesNo,
        MessageBoxImage.Question);
    e.Cancel = (result == MessageBoxResult.No);
}

private void ChildWindow_Closed(object sender, EventArgs e)
{
    Console.WriteLine(@"Closed");
}

ExceptionHandlingSecondaryUIThreadUI线程间异常处理

clipboard.png

在主窗口事件中处理Button.Click,其中创建第二个线程(后台、STA方式),并执行一个委托方法。

// THIS EVENT HANDLER RUNS ON THE MAIN UI THREAD
        private void startSecondaryUIThreadButton_Click(object sender, RoutedEventArgs e)
{
    // Creates and starts a secondary thread in a single threaded apartment (STA)
    var thread = new Thread(MethodRunningOnSecondaryUIThread);
    thread.SetApartmentState(ApartmentState.STA);
    thread.IsBackground = true;
    thread.Start();
}

第二线程中创建第二个窗口,并保持当前线程继续运行Dispatcher.Run()

  1. 窗口显示需要在Dispatcher.Run()之前,后者需在最后,标识其线程开始一直运作。
  2. 在此第二线程若捕获异常,则委托 调用程序主UI线程(STA模式)同步处理异常。完成后之后至此自动关闭此第二线程。
// THIS METHOD RUNS ON A SECONDARY UI THREAD (THREAD WITH A DISPATCHER)
private void MethodRunningOnSecondaryUIThread()
{
    var secondaryUiThreadId = Thread.CurrentThread.ManagedThreadId;
    try
    {
        // On secondary thread, show a new Window before starting a new Dispatcher
        // ie turn secondary thread into a UI thread
        var window = new SecondaryUIThreadWindow();
        window.Show();
        Dispatcher.Run();
    }
    catch (Exception ex)
    {
        // Dispatch the exception back to the main ui thread and reraise it
        Application.Current.Dispatcher.Invoke(
            DispatcherPriority.Send,
            (DispatcherOperationCallback) delegate
            {
                // THIS CODE RUNS BACK ON THE MAIN UI THREAD
                string msg = $"Exception forwarded from secondary UI thread {secondaryUiThreadId}.";
                throw new Exception(msg, ex);
            }
            , null);

        // NOTE - Application execution will only continue from this point
        //        onwards if the exception was handled on the main UI thread
        //        by Application.DispatcherUnhandledException
    }
}
}

第二线程中创建的第二个窗口,处理其按钮点击事件时,发出异常,同时窗口自动关闭。

  1. 发出的异常由后台第二线程(ID:3)接收到,并委托主UI线程(ID:1)处理并发出,此时第二UI线程已完成任务关闭。
  2. 此时回到主UI线程:接收到异常,同时发出新异常信息(包含其线程ID:1)显示出来。

3.流程是:第二窗口发出异常(同时窗口关闭)》第二UI线程(ID:3)收到异常信息,并委托第一UI线程(ID:1)处理异常并发出显示信息。
以下为第二窗口发出异常代码:
ps:DispatcherObject.Dispatcher 中唯一的属性Dispatcher关联的线程是ID:1(主UI线程)

private void raiseExceptionOnSecondaryUIThreadButton_Click(object sender, RoutedEventArgs e)
{
    // Raise an exception on the secondary UI thread
    string msg = $"Exception raised on secondary UI thread {Dispatcher.Thread.ManagedThreadId}.";
    throw new Exception(msg);
}

ProcessingCommandLineArguments处理命令行参数

详细类见下一章介绍。
clipboard.png

在App.Startup事件附加处理方法。

  1. 设置静态Hashtable变量储存参数值对。
  2. 判断是否有命令行参数,无责passed
  3. 循环读取StartupEventArgs的数据,进行字符串匹配,储存,并在主窗口显示
public static Hashtable CommandLineArgs = new Hashtable();

private void App_Startup(object sender, StartupEventArgs e)
{
    // Don't bother if no command line args were passed
    // NOTE: e.Args is never null - if no command line args were passed, 
    //       the length of e.Args is 0.
    if (e.Args.Length == 0) return;

    // Parse command line args for args in the following format:
    //   /argname:argvalue /argname:argvalue /argname:argvalue ...
    //
    // Note: This sample uses regular expressions to parse the command line arguments.
    // For regular expressions, see:
    // http://msdn.microsoft.com/library/en-us/cpgenref/html/cpconRegularExpressionsLanguageElements.asp
    var pattern = @"(?<argname>/\w+):(?<argvalue>\w+)";
    foreach (var arg in e.Args)
    {
        var match = Regex.Match(arg, pattern);

        // If match not found, command line args are improperly formed.
        if (!match.Success)
            throw new ArgumentException(
                "The command line arguments are improperly formed. Use /argname:argvalue.");

        // Store command line arg and value
        CommandLineArgs[match.Groups["argname"].Value] = match.Groups["argvalue"].Value;
    }
}

主窗口加载时显示命令行数据:

public MainWindow()
{
    InitializeComponent();

    foreach (string key in App.CommandLineArgs.Keys)
    {
        commandLineArgsListBox.Items.Add(key + ": " + App.CommandLineArgs[key]);
    }
}

SingleInstanceDetection单实例查验

实现效果:
打开程序实例都是同一个,俗称单开窗口
文件:
clipboard.png

实现步骤:
1、在应用程序入口运行VB的SingleInstanceManager : WindowsFormsApplicationBase.Run()

public class EntryPoint
{
    [STAThread]
    public static void Main(string[] args)
    {
        var manager = new SingleInstanceManager();
        manager.Run(args);
    }
}

// Using VB bits to detect single instances and process accordingly:
//  * OnStartup is fired when the first instance loads
//  * OnStartupNextInstance is fired when the application is re-run again
//    NOTE: it is redirected to this instance thanks to IsSingleInstance

2、在VB环境下检查获取应用程序是否第一次、再次运行情况:

  1. 确定一个私有app,在第一次启动程序时初始化并运行app.run(),
  2. 设置WindowsFormsApplicationBse的属性IsSingleInstance=true.
  3. 检查到为再次打开此程序OnStartupNextInstance时,只需激活此App
public class SingleInstanceManager : WindowsFormsApplicationBase
{
    private SingleInstanceApplication _app;

    public SingleInstanceManager()
    {
        IsSingleInstance = true;
    }

    protected override bool OnStartup(StartupEventArgs e)
    {
        // First time app is launched
        _app = new SingleInstanceApplication();
        _app.Run();
        return false;
    }

    protected override void OnStartupNextInstance(StartupNextInstanceEventArgs eventArgs)
    {
        // Subsequent launches
        base.OnStartupNextInstance(eventArgs);
        _app.Activate();
    }
}

私有app继承Application,在wpf程序启动事件时 实例化主窗口并显示。

public class SingleInstanceApplication : Application
{
    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);

        // Create and show the application's main window
        var window = new MainWindow();
        window.Show();
    }

    public void Activate()
    {
        // Reactivate application's main window
        MainWindow.Activate();
    }
}

SkinnedApplication程序皮肤应用

clipboard.png

实现步骤:
1、在App启动时通过LoadComponent(Uri)加载皮肤资源字典文件

private void App_Startup(object sender, StartupEventArgs e)
{
    Properties["Blue"] = (ResourceDictionary) LoadComponent(new Uri("BlueSkin.xaml", UriKind.Relative));
    Properties["Yellow"] = (ResourceDictionary) LoadComponent(new Uri("YellowSkin.xaml", UriKind.Relative));

    // Note: you can also use the following syntax:
    //   Skins["Yellow"] = new YellowSkin()
    // But only as long as you implement the ResourceDictionary using markup and code-behind,
    // use the x:Class attribute in markup, and call InitializeComponent() from code-behind, eg:
    //
    //   <!-- Markup -->
    //   <ResourceDictionary
    //     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    //     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    //     xmlns:local="clr-namespace:SDKSample" 
    //     x:Class="SDKSample.YellowSkin">
    //        ...
    //   </ResourceDictionary>
    //
    //   // Code-behind
    //   public partial class YellowSkin : ResourceDictionary
    //   {
    //     public YellowSkin() { InitializeComponent(); }
    //   }
}

2、在主窗口加载时从应用程序选定一个皮肤属性作为应用程序Resource,此时皮肤资源自动被应用

public MainWindow()
{
    InitializeComponent();

    // Add choices to combo box
    skinComboBox.Items.Add("Blue");
    skinComboBox.Items.Add("Yellow");
    skinComboBox.SelectedIndex = 0;

    // Set initial skin
    Application.Current.Resources = (ResourceDictionary) Application.Current.Properties["Blue"];

    // Detect when skin changes
    skinComboBox.SelectionChanged += skinComboBox_SelectionChanged;
}

若重新选择皮肤,即把应用程序资源重新赋值,从应用程序属性的皮肤款式中

private void skinComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    // Change the skin
    var selectedValue = (string) e.AddedItems[0];
    Application.Current.Resources = (ResourceDictionary) Application.Current.Properties[selectedValue];
}

扩展,如在Resources.MergedDictionaries合并资源进行更改皮肤,应用性更强,而不要更换整个资源值。如:

private void skinComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    // Change the skin
    var selectedValue = (string) e.AddedItems[0];
    if (Application.Current.Resources.MergedDictionaries.Count!=0)
    {
        Application.Current.Resources.MergedDictionaries.Clear();
        Application.Current.Resources.MergedDictionaries.Add((ResourceDictionary)Application.Current.Properties[selectedValue]);
    }
}

unhandledExceptionHandling对未处理异常 进行处理

clipboard.png

1、主窗口引发一个可恢复的异常、一个不可恢复异常,此时DispatcherUnhandledExceptio事件对接收到异常进行处理。

private void raiseRecoverableException_Click(object sender, RoutedEventArgs e)
{
    throw new DivideByZeroException("Recoverable Exception");
}

private void raiseUnecoverableException_Click(object sender, RoutedEventArgs e)
{
    throw new ArgumentNullException(@"Unrecoverable Exception");
}

Application.DispatcherUnhandleException事件处理 接收到的未处理异常

  1. 设置判断是否可恢复异常 bool标识shutdown
  2. 如果不可恢复,需要关闭应用程序,则在异常对话框后决定是否保存数据,最后再写入日记等
  3. 最后e.Handled=true不希望 WPF 继续处理该异常
private void App_DispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
{
    // Process unhandled exception
    var shutdown = false;

    // Process exception
    if (e.Exception is DivideByZeroException)
    {
        // Recoverable - continue processing
        shutdown = false;
    }
    else if (e.Exception is ArgumentNullException)
    {
        // Unrecoverable - end processing
        shutdown = true;
    }

    if (shutdown)
    {
        // If unrecoverable, attempt to save data
        var result =
            MessageBox.Show("Application must exit:\n\n" + e.Exception.Message + "\n\nSave before exit?", "app",
                MessageBoxButton.YesNo, MessageBoxImage.Error);
        if (result == MessageBoxResult.Yes)
        {
            // Save data
        }

        // Add entry to event log
        EventLog.WriteEntry("app", "Unrecoverable Exception: " + e.Exception.Message, EventLogEntryType.Error);

        // Return exit code
        Shutdown(-1);
    }

    // Prevent default unhandled exception processing
    e.Handled = true;
}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值