0

I have this code stolen from somewhere else on this website:

public static class WindowManager
    {
        public static T GetWindow<T>()
        {
            var t = Application.Current.Windows.OfType<T>().FirstOrDefault();
            if (t == null)
            {
                t = (T)Activator.CreateInstance(typeof(T));
            }
            return t;
        }

        public static T CreateOrFocusWindow<T>()
        {
            var t = GetWindow<T>();
            if (t is Window)
            {
                var window = t as Window;
                if (window.Visibility != Visibility.Visible)
                    window.Show();
                if (window.WindowState == WindowState.Minimized)
                    window.WindowState = WindowState.Normal;
                window.Focus();
            }
            return t;
        }
    }

Its simple implementation of one instance of any window. But it creates certain issue.

private async void Button_OnClick(object sender, RoutedEventArgs e)
        {
            await Task.Run(WindowManager.CreateOrFocusWindow<Window1>);
        }

But it returns exception in line var t = Application.Current.Windows.OfType<T>().FirstOrDefault(); - System.InvalidOperationException - The calling thread cannot access this object because a different thread owns it.

I don't have any idea how to solve it without removing WindowManager, but it simplifies code so much I really want to keep it.

Edit

This fixes exception, but UI is locked despite using async/await.

private async void Button_OnClick(object sender, RoutedEventArgs e)
        {
            LoadingOn();
            await Task.Run(() =>
            {
                Dispatcher.Invoke(() => //InvokeAsync changes nothing
                {
                    WindowManager.CreateOrFocusWindow<FitnessWindow>();
                });
            });
            LoadingOff();
        }
  • Why are you using Task.Run? – Clemens Aug 03 '21 at 11:23
  • @Clemens long operation inside constructor. I don't want to lock UI. –  Aug 03 '21 at 11:24
  • CreateOrFocusWindow won't lock anything. – Clemens Aug 03 '21 at 11:25
  • I have used dispatcher to bypass exception but this is exactly what happens, UI is locked for a few seconds. Not the behavior I want. –  Aug 03 '21 at 11:28
  • See the answers to the question marked as duplicate, or the dozens of similar questions and answers. You can't access `Application.Current.Windows` from a thread other than the UI thread. – Clemens Aug 03 '21 at 11:32
  • @Clemens so, its not possible to run task asynchronously in WPF? Thats very bad news. –  Aug 03 '21 at 11:37
  • That is well possible. You must however use the Dispatcher to access UI elements from non-UI threads. – Clemens Aug 03 '21 at 11:41
  • @Clemens I have used dispatcher and UI still gets locked for a few seconds when creating new window. –  Aug 03 '21 at 11:42
  • How are we supposed to help you with this when you aren't showing us your actual code? – Clemens Aug 03 '21 at 11:55
  • @Clemens its not like there is any point, since someone already marked it as duplicate. But if you are actually eager to help (very rare thing on stackoverflow), I will post the code. –  Aug 03 '21 at 11:57
  • Anyway, a new Window has to be created on the UI thread. If its creation takes time, try to move all unnecessary code out of its constructor. – Clemens Aug 03 '21 at 11:57
  • @Clemens this sounds like a plan but my `MainWindow` will become huge mess, because I will have to gather a lot of data for each window before its created. Now its clean and tidy (each window gets own data in own code behind). –  Aug 03 '21 at 11:59
  • Move that code into an async Loaded event handler where you may run a Task. – Clemens Aug 03 '21 at 12:04
  • Task.Run with only Dispatcher.Invoke is totally pointless. – Clemens Aug 03 '21 at 12:06
  • @Clemens never heard about `async loaded event`, checking google. –  Aug 03 '21 at 12:25
  • A handler of the Window's Loaded event, declared as `async void`, with an `await Task.Run(...)` call, just like your Click handler. – Clemens Aug 03 '21 at 12:34
  • @Clemens ok this is brilliant and elegant. But I do not want to open the window until data is ready. Otherwise I would have to move loading spinner to each new window, and I'm pretty lazy. –  Aug 03 '21 at 12:47
  • Well, then you have to prepare the Window state before creating the Window. It should be clear now that the constructor is the wrong place, as it must be called in the UI thread and will inevitably block it. – Clemens Aug 03 '21 at 12:53

0 Answers0