Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get a static reference to a WPF Window?

I've tried a huge amount of ways to get a static reference of my window across my program. I need to access all of its members at runtime from different classes, so a static reference is needed.

What I'd like to have is something like Program.Window1, where Core is static and MyWindow is one of its static members.

In WinForms, I usually declare my static form in Program.cs, but this does not seem to work with WPF and their custom "App.xaml" ApplicationDefinition.

How can I do it?

Note: I have tried a number of ways already: using a direct call to a new window (i.e. Program.Window1 = new Window1()) will not work, as I get some thread invalidity exception. As I understand so far, only ApplicationDefinitions can launch windows in WPF.

Here is the exception whenever I try to create a window "by code" and not by the default XAML ApplicationDefinition's StartupUri:

The calling thread must be STA, because many UI components require this.

like image 427
Lazlo Avatar asked Feb 13 '10 21:02

Lazlo


3 Answers

Create a static class that can contain the window object, and then when the window is created, have it pass itself to the static class, from then on the static class can hand out the window object to interested parties even though the window object itself is not static. Something like this. There is no need for your form to be static, you just need a static place to hold the form object.

public class Core
{
     internal static MyWindowClass m_Wnd = null;

     // call this when your non-static form is created
     //
     public static void SetWnd(MyWindowClass wnd)
     {
         m_Wnd = wnd;
     }

     public static MyWindow { get { return m_Wnd; } }
}
like image 122
John Knoeller Avatar answered Oct 21 '22 22:10

John Knoeller


Try this ((MainWindow)App.Current.Windows[0]).MainCanvas.

like image 35
Friend Avatar answered Oct 21 '22 21:10

Friend


It is absolutely possible to instantiate your own windows with WPF. But yes, you would have to be on the "UI thread" (which is a STA thread).

For example, say that we would like to have a property on our App class that exposes some window.

    public partial class App
    {
        private static Window _myWindow;

        public static Window1 MyWindow
        {
            get
            {
                if (_myWindow == null)
                    _myWindow = new Window1();
                return _myWindow;
            }
       }
    }

The problem with this code is, as you have experienced, depending on what thread is calling the MyWindow getter, new Window1() will fail if the thread is not an STA.

To make sure the window is created on the right thread, enter the Dispatcher object. This object is used throughout WPF to make sure that communication between UI elements is done on the correct thread.

Back to our new Window1, we can use the App.Dispatcher object to make sure the new operation is done on the main application thread, like this:

        public static Window1 MyWindow
        {
            get
            {
                if (_myWindow == null)
                {
                     var window = 
                             Application.Current.Dispatcher.Invoke(
                               new Func<Window1>(() => new Window1()));

                    _myWindow = (Window1)window;
                }

                return _myWindow;
            }
       }

Here, I get a hold on the current application's Dispatcher object and calls Invoke with a delegate that does the actual newing. Invoke ensures that my delegate is executed on the correct thread, and returns the result. Voila, the window is created without the dreaded STA error.

Now, what you must have in mind is that further calls made on the MyWindow instance must also be made on the correct thread. To avoid littering your code with calls to Dispatcher.Invoke, it could be useful to wrap the window instance behind a simple API. E.g. a Show method could be implemented like this, making sure to marshal the Show call via the Dispatcher object of the window:

        public static void ShowMyWindow()
        {
            MyWindow.Dispatcher.Invoke(new Action(MyWindow.Show));
        }
like image 21
Peter Lillevold Avatar answered Oct 21 '22 20:10

Peter Lillevold