2015-09-27 3 views
1

я нашел это решение для проверки, если окно открыто:вспомогательный метод проверки, если открыто окно в другом потоке

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, который содержит ссылку на Диспетчер, связанный с потоком, в котором было создано окно. что-то вроде: _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 утра в Индии. И если мой ответ помог вам здесь, подтвердите и отметьте его как ответ plz. – AnjumSKhan

+0

Конечно, спасибо вам за помощь! Это очень ценится. – konrad

0

Если вы вернетесь в окно из метода IsWindowOpen. U может использовать Invoke или BeginInvoke в окне, чтобы отправить работу над потоком, в котором было создано окно.

1

Dispatcher здесь не помогает, потому что, когда окно создается в другом потоке, оно не содержится в коллекции 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))); 
} 

EDIT Вот то, что работает в настоящее время, но может измениться в будущем, так как уже упоминалось выше, использовать его на свой страх и риск

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 Я опубликовал что-то, что могло бы помочь вам решить вашу проблему на данный момент. –

Смежные вопросы