DLL从控制台应用程序显示窗口麻烦

时间:2014-01-21 22:04:36

标签: c# wpf dll console-application

我创建了一个WPF应用程序,然后通过删除app.xaml并将构建设置为类库将其转换为DLL。我正在使用C#Console应用程序来测试DLL。对于我的第一个测试,如果我将mainwindow.show()放在mainWindow = new MainWindow()下面的try块中,我能够让应用程序显示正常。现在我需要能够预加载wpf应用程序,只在需要时才显示它,而不是每次都要加载它。我遇到的问题是显示wpf应用程序的调用是在不同的线程上,而ShowWPFAppDLL()主窗口是null。我能用这种方式工作吗?

控制台应用程序:

namespace ConsoleApp
{
    class Program
    {
        static WPFAppDLL.LoadWpfAppDll loader = new WPFAppDLL.LoadWpfAppDll();

        static void Main(string[] args)
        {
            Thread worker = new Thread(new ThreadStart(LoadWpfApp));
            worker.SetApartmentState(ApartmentState.STA);
            worker.Name = "WpfThread";
            worker.IsBackground = true;
            worker.Start();

            Thread.Sleep(15000);
            ShowWpfApp();
            worker.Join();
        }

        private static void ShowWpfApp()
        {
            loader.ShowWPFAppDLL();
        }

        private static void LoadWpfApp()
        {
            loader.Load();
        }
    }
}

WPF应用程序(DLL):

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;

namespace WPFAppDLL
{
    public class LoadWpfAppDll
    {
        MainWindow mainWindow = null;

        public void Load(string[] args)
        {
            Application application = new Application();

            application.Resources.MergedDictionaries.Add(new ResourceDictionary { Source = new Uri("pack://application:,,,/WPFAppDLL;component/Resources/DataTemplates/DataTemplate.xaml", UriKind.RelativeOrAbsolute) });
            application.Resources.MergedDictionaries.Add(new ResourceDictionary { Source = new Uri("pack://application:,,,/WPFAppDLL;component/Resources/Styles/GlobalStyles.xaml", UriKind.RelativeOrAbsolute) });
            application.Resources.MergedDictionaries.Add(new ResourceDictionary { Source = new Uri("pack://application:,,,/WPFAppDLL;component/Resources/Styles/ImageResources.xaml", UriKind.RelativeOrAbsolute) });
            application.Resources.MergedDictionaries.Add(new ResourceDictionary { Source = new Uri("pack://application:,,,/WPFAppDLL;component/Resources/Styles/BrushesStyles.xaml", UriKind.RelativeOrAbsolute) });
            application.Resources.MergedDictionaries.Add(new ResourceDictionary { Source = new Uri("pack://application:,,,/WPFAppDLL;component/Resources/Styles/TabControlStyles.xaml", UriKind.RelativeOrAbsolute) });
            application.Resources.MergedDictionaries.Add(new ResourceDictionary { Source = new Uri("pack://application:,,,/WPFAppDLL;component/Resources/Styles/ButtonStyles.xaml", UriKind.RelativeOrAbsolute) });
            application.Resources.MergedDictionaries.Add(new ResourceDictionary { Source = new Uri("pack://application:,,,/WPFAppDLL;component/Resources/Styles/LabelStyles.xaml", UriKind.RelativeOrAbsolute) });
            application.Resources.MergedDictionaries.Add(new ResourceDictionary { Source = new Uri("pack://application:,,,/WPFAppDLL;component/Resources/Styles/TextboxStyles.xaml", UriKind.RelativeOrAbsolute) });
            application.Resources.MergedDictionaries.Add(new ResourceDictionary { Source = new Uri("pack://application:,,,/WPFAppDLL;component/Resources/Styles/ComboBoxStyles.xaml", UriKind.RelativeOrAbsolute) });
            application.Resources.MergedDictionaries.Add(new ResourceDictionary { Source = new Uri("pack://application:,,,/WPFAppDLL;component/Resources/Styles/DatagridStyles.xaml", UriKind.RelativeOrAbsolute) });
            application.Resources.MergedDictionaries.Add(new ResourceDictionary { Source = new Uri("pack://application:,,,/WPFAppDLL;component/Resources/Styles/GroupBoxStyles.xaml", UriKind.RelativeOrAbsolute) });
            application.Resources.MergedDictionaries.Add(new ResourceDictionary { Source = new Uri("pack://application:,,,/WPFAppDLL;component/Resources/Styles/CheckBoxStyles.xaml", UriKind.RelativeOrAbsolute) });
            application.Resources.MergedDictionaries.Add(new ResourceDictionary { Source = new Uri("pack://application:,,,/WPFAppDLL;component/Resources/Styles/RadioButtonStyles.xaml", UriKind.RelativeOrAbsolute) });
            application.Resources.MergedDictionaries.Add(new ResourceDictionary { Source = new Uri("pack://application:,,,/WPFAppDLL;component/Resources/Styles/Converters.xaml", UriKind.RelativeOrAbsolute) });
            application.Resources.MergedDictionaries.Add(new ResourceDictionary { Source = new Uri("pack://application:,,,/WPFAppDLL;component/Resources/Styles/ListBoxStyles.xaml", UriKind.RelativeOrAbsolute) });
            application.Resources.MergedDictionaries.Add(new ResourceDictionary { Source = new Uri("pack://application:,,,/WPFAppDLL;component/Resources/Styles/MessageBoxStyles.xaml", UriKind.RelativeOrAbsolute) });

            SplashScreenWindow splashWindow = new SplashScreenWindow();
            splashWindow.WindowStartupLocation = WindowStartupLocation.CenterScreen;
            splashWindow.Show();

            EventManager.RegisterClassHandler(typeof(TextBox), TextBox.PreviewMouseLeftButtonDownEvent, new MouseButtonEventHandler(SelectivelyIgnoreMouseButton));
            EventManager.RegisterClassHandler(typeof(TextBox), TextBox.GotKeyboardFocusEvent, new RoutedEventHandler(SelectAllText));
            EventManager.RegisterClassHandler(typeof(TextBox), TextBox.MouseDoubleClickEvent, new RoutedEventHandler(SelectAllText));

            try
            {
                mainWindow = new MainWindow();
                mainWindow.WindowStartupLocation = WindowStartupLocation.CenterScreen;
                splashWindow.Close();
                application.Run();
            }
            catch (Exception ex)
            {
                splashWindow.Close();
                MessageBox.Show("Error starting application:" + Environment.NewLine + ex.ToString(), "Error Message", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }

        public void ShowWPFAppDLL()
        {
            if (mainWindow != null)
            {
                mainWindow.Show();
            }
        }

        private void SelectivelyIgnoreMouseButton(object sender, MouseButtonEventArgs e)
        {
            DependencyObject parent = e.OriginalSource as UIElement;
            while (parent != null && !(parent is TextBox))
            {
                parent = VisualTreeHelper.GetParent(parent);
            }

            if (parent != null)
            {
                TextBox textBox = (TextBox)parent;
                if (!textBox.IsKeyboardFocusWithin)
                {
                    textBox.Focus();
                    e.Handled = true;
                }
            }
        }

        private void SelectAllText(object sender, RoutedEventArgs e)
        {
            TextBox textBox = e.OriginalSource as TextBox;
            if (textBox != null)
            {
                textBox.SelectAll();
            }
        }
    }
}

1 个答案:

答案 0 :(得分:1)

您可以实现 ManualResetEvent ShowWPFAppDLL 正在等待,并且在 mainWindow 实例化后设置。

此外,您必须确保无论您使用 mainWindow 执行的操作都发生在运行负责 mainWindow 的调度程序的线程上。

您的代码可能与此类似:

public class LoadWpfAppDll
{
    private readonly ManualResetEvent _evtMainWindowInstantiated = new ManualResetEvent(false);

    private MainWindow mainWindow = null;


    public void Load(string[] args)
    {
        try
        {
            ......all the stuff you do in Load()...

            try
            {
                mainWindow = new MainWindow();
                mainWindow.WindowStartupLocation = WindowStartupLocation.CenterScreen;
                splashWindow.Close();

                application.Startup +=
                    (sender, e) => _evtMainWindowInstantiated.Set();

                application.Run();
            }
            catch (Exception ex)
            {
                splashWindow.Close();
                MessageBox.Show("Error starting application:" + Environment.NewLine + ex.ToString(), "Error Message", MessageBoxButton.OK, MessageBoxImage.Error);

                mainWindow = null;
            }
        }
        finally
        {
            //
            // Ensures that the _evtMainWindowInstantiated is always set, even in
            // failure case. If this would not be done, a failure of the Load
            // method could block other threads waiting on this ManualResetEvent
            // in one of the public methods below.
            //
            _evtMainWindowInstantiated.Set();
        }
    }


    private void InvokeOnMainWindowThread(Action action)
    {
        _evtMainWindowInstantiated.WaitOne();

        if (mainWindow == null)
        {
            ...something bad happened in Load()...
            ...do error handling or just return, whatever is appropriate...
        }

        //
        // Make sure that the action is invoked on the mainWindow thread.
        // If InvokeOnMainWindowThread is already called on the
        // mainWindow thread, the action should not be queued by the
        // dispatcher but should be executed immediately.
        //
        if (mainWindow.Dispatcher.CheckAccess()) action();
        else mainWindow.Dispatcher.Invoke(action, null);
    }


    public void ShowWPFAppDLL()
    {
        InvokeOnMainWindowThread(mainWindow.Show);
    }
}

请注意, _evtMainWindowInstantiated 是一个永远不会被重置的ManualResetEvent。因此,一旦在 Load()中设置方法_evtMainWindowInstantiated.WaitOne()将永远不会阻止/再次等待。

另外,我故意引入了一个 InvokeOnMainWindowThread 方法,该方法负责处理 _evtMainWindowInstantiated 并在 mainWindow 的调度程序线程上执行操作。如果您需要实现多个公共方法,例如 ShowWPFAppDLL ,那么为此设置专用方法是值得的。

与此相关,我将 mainWindow 设为私有,因为访问它需要由InvokeOnMainWindowThread管理。允许其他类直接访问 mainWindow 可能会导致与多线程相关的问题,或者因为 Load()尚未完成。

如果你想从修改mainWindow的公共方法返回一些结果,你可以实现 InvokeOnMainWindowThread 的重载,它以 Func< T> 委托作为参数

作为最后一点,我建议您让WpfAppDll库也为主窗口创建和设置线程。让“外部世界”设置将运行 mainWindow 的调度程序(又称消息循环)的线程看起来容易出错。