我创建了一个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();
}
}
}
}
答案 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 的调度程序(又称消息循环)的线程看起来容易出错。