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
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?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With