2014-01-21 55 views
0

我創建了一個WPF應用程序,然後通過刪除app.xaml並將構建設置爲類庫將其轉換爲DLL。我正在使用C#控制檯應用程序來測試DLL。對於我的第一次測試,我能夠讓應用程序顯示得很好,如果我把mainwindow.show()放在mainWindow = new MainWindow()下的try塊中。現在我需要能夠預加載wpf應用程序,並且只在需要時顯示它,而不必每次都加載它。我遇到的問題是顯示wpf應用程序的調用位於不同的線程上,並且ShowWPFAppDLL()主窗口爲空。任何方式我可以得到這個工作?從控制檯應用程序顯示DLL的問題

控制檯應用程序:

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

那麼上面的代碼到底在哪裏失敗? – Fayilt

+0

顯然,* mainWindow *僅在* Load *方法中初始化。在使用* mainWindow *之前,您需要調用它一次。當* mainWindow *爲null時,什麼會阻止你從方法ShowWPFAppDLL中調用* Load *?這真的是你的問題嗎? – elgonzo

+0

@Fayilt當我調用ShowWPFAppDLL()時,mainWindow爲null,所以它不會像我想要的那樣顯示。 – Danno

回答

1

你可以實現一個ManualResetEvent的,這ShowWPFAppDLL等待並在主窗口後設置被實例化。

而且,你必須確保,無論你做主窗口發生運行調度員負責主窗口的線程上。

你的代碼可能看起來與此類似:

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的。因此,一旦將其設置在加載()方法_evtMainWindowInstantiated.WaitOne()將永遠不會阻止/再次等待。

另外,我故意引入一個InvokeOnMainWindowThread方法這需要處理_evtMainWindowInstantiated和執行上主窗口的調度線程進行動作的照顧。如果您需要實施多個公共方法(如ShowWPFAppDLL),則需要專門的方法。

與此相關,我製作了mainWindow私有的,因爲訪問它需要由InvokeOnMainWindowThread管理。允許其他類直接訪問mainWindow可能會導致與多線程相關的問題,或者因爲Load()尚未完成。

如果你想回到您的公共方法與主窗口擺弄一些成果,可能實現InvokeOnMainWindowThread的過載,這需要Func鍵<牛逼>委託作爲參數。

作爲最後一點,我建議你讓WpfAppDll庫也創建並設置主窗口的線程。看起來很容易讓我的「外部世界」設置線程,它將運行mainWindow的調度程序(又名消息循環)。

+0

得到它的工作,感謝您的幫助 – Danno

+0

@Danno,很高興我可以幫助:) – elgonzo

+0

@Danno,還有一件事:別忘了做適當的異常處理。如果Application.Run由於傳遞給Dispatcher.Invoke的操作而導致未處理的異常而退出,那麼您可能很難將應用程序保持在可工作狀態(執行此操作的一種方法可能是通過Application.DispatcherUnhandledException)。 – elgonzo

相關問題