using System; using System.ComponentModel; using System.Windows.Forms; namespace Nuclex.Windows.Forms { /// /// Proxy stand-in to delay checking for the main window until it has been created /// /// /// /// The issue: when the view model for the main window is created, the main window /// may only exist as a .NET object, without the underlying operating system window /// (done in checkable via /// ). Not only will things /// like fail, we can't /// even locate the main window at that stage. /// /// /// Thus, if the main window cannot be found at the time a view model is created, /// this late-checking synchronizer will jump into its place and re-check for /// the main window only when something needs to be executed in the UI thread. /// /// class LateCheckedSynchronizer : ISynchronizeInvoke { /// Initializes a new late-checked main window synchronizer /// public LateCheckedSynchronizer(Action uiContextFoundCallback) { this.uiContextFoundCallback = uiContextFoundCallback; } /// Finds the application's main window /// Main window of the application or null if none has been created /// /// The application's main window, if it has been created yet /// public static Form GetMainWindow() { IntPtr mainWindowHandle = System.Diagnostics.Process.GetCurrentProcess().MainWindowHandle; // We can get two things: a list of all open windows and the handle of // the window that the process has registered as main window. Use the latter // to pick the correct window from the former. FormCollection openForms = Application.OpenForms; int openFormCount = openForms.Count; for(int index = 0; index < openFormCount; ++index) { Form form = openForms[index]; IntPtr handle; if(form.InvokeRequired) { handle = (IntPtr)form.Invoke(new Func(getWindowHandle), form); } else { handle = getWindowHandle(form); } if(handle != IntPtr.Zero) { if(handle == mainWindowHandle) { return form; } } } // No matching main window found: use the first one in good faith or fail. if(openFormCount > 0) { return openForms[0]; } else { return null; } } /// Checks whether the calling thread needs to use Invoke() public bool InvokeRequired { get { return getMainWindowOrFail().InvokeRequired; } } /// Schedules a method to be run by the main UI thread /// Method that will be scheduled to run /// Arguments that will be passed to the method /// An asynchronous result handle that can be used to track the call public IAsyncResult BeginInvoke(Delegate method, object[] args) { return getMainWindowOrFail().BeginInvoke(method, args); } /// Waits for a call scheduled on the main UI thread to complete /// Asynchronous result handle returned by BeginInvoke() /// The value returned by the method ran in the main UI thread public object EndInvoke(IAsyncResult result) { return getMainWindowOrFail().EndInvoke(result); } /// Executes a method on the main UI thread and waits for it to complete /// Method that will be run by the main UI thread /// Arguments that will be passed to the method /// The value returned by the method public object Invoke(Delegate method, object[] arguments) { return getMainWindowOrFail().Invoke(method, arguments); } /// Retrieves the application's current main window /// The application's current main window /// /// If there is no main window, an exception will be thrown /// private Form getMainWindowOrFail() { Form mainWindow = GetMainWindow(); if(mainWindow == null) { throw new InvalidOperationException( "Could not schedule work for the UI thread because no WinForms UI main window " + "was found. Create a main window first or specify the UI synchronization context " + "explicitly to the view model." ); } if(this.uiContextFoundCallback != null) { this.uiContextFoundCallback(mainWindow); this.uiContextFoundCallback = null; } return mainWindow; } /// Returns a Form's window handle without forcing its creation /// Form whose window handle will be returned /// The form's window handle of IntPtr.Zero if it has none private static IntPtr getWindowHandle(Form form) { if(form.IsHandleCreated) { return form.Handle; } else { return IntPtr.Zero; } } /// Called when the late-checked synchronizer finds the main window private Action uiContextFoundCallback; } } // namespace Nuclex.Windows.Forms