In WPF, how to set a Window Owner of a Window build on another thread (another Dispatcher)

c#, dispatcher, multithreading, wpf

Solution

Answer above was correct. But I will try to summarize:

[DllImport("user32.dll")]
static extern int SetWindowLong(IntPtr hwnd, int index, int newStyle);

public static void SetOwnerWindowMultithread(IntPtr windowHandleOwned, IntPtr intPtrOwner)
{
    if (windowHandleOwned != IntPtr.Zero && intPtrOwner != IntPtr.Zero)
    {
        SetWindowLong(windowHandleOwned, GWL_HWNDPARENT, intPtrOwner.ToInt32());
    }
}

Code to get WPF handler:

public static IntPtr GetHandler(Window window)
{
    var interop = new WindowInteropHelper(window);
    return interop.Handle;
}

Note that window should be initialized before set owner call! (can be set in window.Loaded or window.SourceInitialized event)

var handler = User32.GetHandler(ownerForm);

var thread = new Thread(() =>
{
    var window = new DialogHost();
    popupKeyboardForm.Show();
    SetOwnerWindowMultithread(GetHandler(popupKeyboardForm), handler);
    Dispatcher.Run();
});

thread.IsBackground = true;
thread.Start();

Also SetParent can be used. Than you dont need to convert handlers:

[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);

Note that parent and owner have different meanings. Win32 window Owner vs window Parent?

Problem

I got the following exception: InvalidOperationException : The calling thread cannot access this object because a different thread owns it. when I try to set the Owner of a window that is build on another thread than the Owner. I know that I can only update UI object from the proper thread, but why I can't just set the owner if it come from another thread? Can I do it on another way ? I want to make the progress window the only one which can have input entries. This is the portion of code where bug occurs: ``` public partial class DlgProgress : Window { // ****************************************************************** private readonly DlgProgressModel _dlgProgressModel; // ****************************************************************** public static DlgProgress CreateProgressBar(Window owner, DlgProgressModel dlgProgressModel) { DlgProgress dlgProgressWithProgressStatus = null; var listDlgProgressWithProgressStatus = new List<DlgProgress>(); var manualResetEvent = new ManualResetEvent(false); var workerThread = new ThreadEx(() => StartDlgProgress(owner, dlgProgressModel, manualResetEvent, listDlgProgressWithProgressStatus)); workerThread.Thread.SetApartmentState(ApartmentState.STA); workerThread.Start(); manualResetEvent.WaitOne(10000); if (listDlgProgressWithProgressStatus.Count > 0) { dlgProgressWithProgressStatus = listDlgProgressWithProgressStatus[0]; } return dlgProgressWithProgressStatus; } // ****************************************************************** private static void StartDlgProgress(Window owner, DlgProgressModel progressModel, ManualResetEvent manualResetEvent, List<DlgProgress> listDlgProgressWithProgressStatus) { DlgProgress dlgProgress = new DlgProgress(owner, progressModel); listDlgProgressWithProgressStatus.Add(dlgProgress); dlgProgress.ShowDialog(); manualResetEvent.Set(); } // ****************************************************************** private DlgProgress(Window owner, DlgProgressModel dlgProgressModel) { if (owner == null) { throw new ArgumentNullException("Owner cannot be null"); } InitializeComponent(); this.Owner = owner; // Can't another threads owns it exception ```

Original source

Related problems