1

I have found this solution for checking if a window is open:

How do I know if a WPF window is opened

It's throwing an error back at me since my wpf window is on another thread. Is there a way to still use it?

Solution:

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
}
Community
  • 1
  • 1
konrad
  • 3,544
  • 4
  • 36
  • 75
  • What exception are you getting? – Sievajet Sep 27 '15 at 22:37
  • Window derives from DispatcherObject which holds a reference to a Dispatcher associated with the thread the window was created on. something like : _window.Dispatcher,BeginInvoke( () => ,,,,,) ; – eran otzap Sep 27 '15 at 22:39
  • @Sievajet the wpf window was created on another thread so I am getting an exception that I cannot access it from a different thread. At least with this method.I would like to call it from the main thread. – konrad Sep 27 '15 at 22:44

3 Answers3

2

I have created a sample application solving your problem after spending entire day.

Solution can be downloaded here

What it does : Click button to create window on new thread. A new window is created for you on new thread. The moment this new window is created, this button in your mainwindow is disabled. When you close your new window, creation button in your mainwindow is enabled again.

If it doesn't fit your needs, tell your requirements, I will improve it. Same can be done using pure Win32 functions too without using our event bridge class. I am working on it. And I will post win32 version soon.

  1. I am creating NewWindow on a separate thread. If you close MainWindow, NewWindow still runs as it is on new thread.
  2. I am keeping it completely separate as no instance is used in MainWindow to point to NewWindow. To solve this issue I am using a Win32 handle.
  3. For NewWindow to send notifications to MainWindow, I am using a static class WindowNotifier with static events. This class acts as the bridge between the two. In NewWindow Closing/Closed/Loaded events are used to fire events.
  4. MainWindow handle various events of this static class to remain updated about NewWindow.
  5. Win32 functions used :

    [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);
        }       
...
AnjumSKhan
  • 9,647
  • 1
  • 26
  • 38
  • I posted a question pertaining a little modification to your method. Can you advise? http://stackoverflow.com/questions/32875806/returning-an-object-from-a-class-in-c-sharp-wpf – konrad Sep 30 '15 at 21:29
  • i am going to sleep now, its 3:01am in India. And if my answer helped u here, upvote and mark it as answer plz,. – AnjumSKhan Sep 30 '15 at 21:31
1

Dispatcher does not help here because when window is created on a different thread, it's not contained in the Application.Windows collection, but in a collection which for some reason is not exposed (called NonAppWindowsInternal). Shortly, there is no official way to do that. Of course you can use reflection on your own risk.

But if your window is on UI thread and you just want to call the function from another thread, then you can use something like this

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

or better change the helper method to be

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 Here is something that works currently, but may change in the future, so as mentioned above, use it on your own risk

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);
    }
}
Ivan Stoev
  • 195,425
  • 15
  • 312
  • 343
  • so what you are saying is that there isnt a way to check if wpf window that was opened in another thread is still open. – konrad Sep 28 '15 at 02:04
  • Unfortunately yes. At least reliable - see the **reflection** note. You an read it this way - it can be done by accessing the internal member via reflection, but this may not work in the future if MS change the implementation at some point. – Ivan Stoev Sep 28 '15 at 02:15
  • @Konrad I've posted something that might help you solving your problem for now. – Ivan Stoev Sep 30 '15 at 07:44
0

If you return the window from your IsWindowOpen method. U can use the Invoke or BeginInvoke on the window, to dispatch the work on the thread where the window was created on.

Sievajet
  • 3,443
  • 2
  • 18
  • 22