2015-09-27 35 views
1

我發現這個解決方案,用於檢查窗口打開時打開:helper方法檢查,如果窗口是在另一個線程

How do I know if a WPF window is opened

它拋出一個錯誤,回到我,因爲我的WPF窗口是在另一個線程。有沒有辦法繼續使用它?

解決方案:

public static bool IsWindowOpen<T>(string name = "") where T : Window 
{ 
return string.IsNullOrEmpty(name) 
    ? Application.Current.Windows.OfType<T>().Any() 
    : Application.Current.Windows.OfType<T>().Any(w => w.Name.Equals(name)); 
} 

if (Helpers.IsWindowOpen<Window>("MyWindowName")) 
{ 
// MyWindowName is open 
} 

if (Helpers.IsWindowOpen<MyCustomWindowType>()) 
{ 
// There is a MyCustomWindowType window open 
} 

if (Helpers.IsWindowOpen<MyCustomWindowType>("CustomWindowName")) 
{ 
// There is a MyCustomWindowType window named CustomWindowName open 
} 
+0

你得到什麼異常? – Sievajet

+0

窗口派生自DispatcherObject,它保存對與創建窗口的線程關聯的Dispatcher的引用。 類似: _window.Dispatcher,BeginInvoke的(()=> ,,,,,); –

+0

@Sievajet wpf窗口在另一個線程上創建,所以我得到一個異常,我無法從另一個線程訪問它。至少用這個方法。我想從主線程調用它。 – konrad

回答

2

我已經創建了一個示例應用程序,花費了整整一天的時間解決了您的問題。

Solution can be downloaded here

做些什麼: 點擊按鈕來創建新的線程窗口。在新線程上爲您創建一個新窗口。創建此新窗口時,主窗口中的此按鈕將被禁用。當你關閉你的新窗口時,主窗口中的創建按鈕再次啓用。

如果它不符合您的需求,請告訴您的要求,我會改進它。在不使用事件橋接類的情況下,也可以使用純Win32函數完成。我正在做。我將盡快發佈win32版本。

  1. 我在單獨的線程上創建NewWindow。如果你關閉了MainWindow,那麼NewWindow仍然會像新線程一樣運行。
  2. 我保持它完全獨立,因爲在MainWindow中沒有實例用於指向NewWindow。要解決此問題,我正在使用Win32句柄。
  3. 對於NewWindow將通知發送到MainWindow,我使用靜態類WindowNotifier與靜態事件。這個班級是兩者之間的橋樑。在NewWindow Closing/Closed/Loaded事件用於觸發事件。
  4. MainWindow處理此靜態類的各種事件以保持有關NewWindow的更新。使用
  5. Win32函數:

    [DllImport("user32.dll")] 
    public static extern IntPtr GetForegroundWindow(); 
    
    [DllImport("user32.dll")] 
    public static extern bool IsWindowVisible(IntPtr hWnd); 
    

ThreadCreator.cs

public static class ThreadCreator 
    { 
     private static NewWindow W; 

     public static void CreateWindow() 
     { 
      Thread t = new Thread(ThreadProc); 
      t.SetApartmentState(ApartmentState.STA); 
      t.Start(); 
     } 

     private static void ThreadProc(object obj) 
     { 
      W = new NewWindow(); 
      W.ShowDialog();    
     } 
    } 

WindowNotifier.cs

public static class WindowNotifier 
    { 
     public static event CreatedDelegateCallback IamCreatedEvent; 
     public delegate void CreatedDelegateCallback(IntPtr handle); 

     public static event ClosingDelegateCallback IamClosingEvent; 
     public delegate void ClosingDelegateCallback (IntPtr handle); 

     public static event ClosedDelegateCallback IamClosedEvent; 
     public delegate void ClosedDelegateCallback(IntPtr handle); 

     public static void OnIamCreated(IntPtr handle) 
     { 
      if (IamCreatedEvent != null) 
       IamCreatedEvent(handle); 
     }   

     public static void OnIamClosing(IntPtr handle) 
     { 
      if (IamClosingEvent != null) 
       IamClosingEvent(handle); 
     } 

     public static void OnIamClosed(IntPtr handle) 
     { 
      if (IamClosedEvent != null) 
       IamClosedEvent(handle); 
     } 
    } 

MainWindow.xaml.cs

... 
    void WindowNotifier_IamCreatedEvent(IntPtr handle) 
      { 
       HandleOfWindowOnNewThread = handle; 

       Debug.WriteLine(string.Format("I am created : {0}", handle)); 

       btnCreateNewWindow.Dispatcher.Invoke(() => btnCreateNewWindow.IsEnabled = false); 
      } 

      void WindowNotifier_IamClosedEvent(IntPtr handle) 
      { 
       if (HandleOfWindowOnNewThread == handle) 
        HandleOfWindowOnNewThread = IntPtr.Zero; 

       Debug.WriteLine(string.Format("I am closed : {0}", handle)); 

       btnCreateNewWindow.Dispatcher.Invoke(() => btnCreateNewWindow.IsEnabled = true); 
      } 
... 

NewWindow.xaml.cs

... 
     private void Window_Closed(object sender, EventArgs e) 
     { 
      WindowNotifier.OnIamClosed(Handle); 
     } 

     private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e) 
     { 
      WindowNotifier.OnIamClosing(Handle); 
     } 

     // To get correct handle we need to ensure Window is fully created and active 
     private void Window_Loaded(object sender, RoutedEventArgs e) 
     { 
      _handle = GetForegroundWindow(); 

      WindowNotifier.OnIamCreated(Handle); 
     }  
... 
+0

我發佈了一個有關你的方法稍作修改的問題。你能建議嗎? http://stackoverflow.com/questions/32875806/returning-an-object-from-a-class-in-c-sharp-wpf – konrad

+0

我要去印度到現在睡覺,它的3:01。如果我的回答在這裏幫助你,upvote並將其標記爲答案plz。 – AnjumSKhan

+0

當然,謝謝你的幫助!非常感謝。 – konrad

0

如果您從IsWindowOpen方法返回的窗口。 U可以使用窗口上的InvokeBeginInvoke來分配創建窗口的線程上的工作。

1

調度程序在這裏沒有幫助,因爲當窗口在不同的線程上創建時,它不包含在Application.Windows集合中,但在由於某種原因未公開的集合(稱爲NonAppWindowsInternal)中。很快,沒有官方的方式來做到這一點。當然你可以用自己的風險來反思。

但是,如果你的窗口是在UI線程,你只是想從另一個調用線程的功能,那麼你可以使用這樣的事情

Application.Current.Dispatcher.Invoke(() => IsWindowOpen<...>(...)) 

或更好改變助手方法是

public static bool IsWindowOpen<T>(string name = "") where T : Window 
{ 
    return Application.Current.Dispatcher.Invoke(() => string.IsNullOrEmpty(name) 
     ? Application.Current.Windows.OfType<T>().Any() 
     : Application.Current.Windows.OfType<T>().Any(w => w.Name.Equals(name))); 
} 

編輯下面是一些作品目前,但在未來可能改變,所以如上所述,用它在你自己的風險

public static class WindowUtils 
{ 
    public static bool IsWindowOpen<T>(string name = "") where T : Window 
    { 
     return FindWindow<T>(name) != null; 
    } 
    public static T FindWindow<T>(string name = "") where T : Window 
    { 
     return FindWindow<T>(WindowsInternal, name) ?? FindWindow<T>(NonAppWindowsInternal, name); 
    } 
    private static T FindWindow<T>(Func<Application, WindowCollection> windowListAccessor, string name = "") where T : Window 
    { 
     bool matchName = !string.IsNullOrEmpty(name); 
     var windowList = windowListAccessor(Application.Current); 
     for (int i = windowList.Count - 1; i >= 0; i--) 
     { 
      var window = windowList[i] as T; 
      if (window != null && (!matchName || window.Name == name)) return window; 
     } 
     return null; 
    } 
    private static readonly Func<Application, WindowCollection> WindowsInternal = GetWindowCollectionAccessor("WindowsInternal"); 
    private static readonly Func<Application, WindowCollection> NonAppWindowsInternal = GetWindowCollectionAccessor("NonAppWindowsInternal"); 
    private static Func<Application, WindowCollection> GetWindowCollectionAccessor(string propertyName) 
    { 
     var property = typeof(Application).GetProperty(propertyName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); 
     return (Func<Application, WindowCollection>)Delegate.CreateDelegate(
      typeof(Func<Application, WindowCollection>), property.GetMethod); 
    } 
} 
+0

所以你在說什麼是沒有辦法來檢查在另一個線程中打開的wpf窗口是否仍然打開。 – konrad

+0

不幸的是。至少可靠 - 請參閱**反射**注。您可以通過這種方式閱讀 - 可以通過反射訪問內部成員來完成,但如果MS在某個時間點更改實施,將來可能無法使用。 –

+0

@Konrad我發佈了一些可以幫助你解決問題的東西。 –

相關問題