2010-10-06 65 views
3

創建我有下面的代碼,這在它自己的專用UI線程上運行一個WPF窗口:WPF設置所有者的窗口,在自己的專用UI線程

// Create the dedicated UI thread for AddEditPair window 
Thread addEditPairThread = new Thread(() => 
{ 
    // Initialise the add edit pair window 
    addEditPair = new AddEditPair(this); 
    addEditPair.PairRecordAdded += new EventHandler<PairRecordEventArgs>(addEditPair_PairRecordAdded); 
    addEditPair.PairRecordEdited += new EventHandler<PairRecordEventArgs>(addEditPair_PairRecordEdited); 

    // Force AddEditPair to run on own UI thread 
    System.Windows.Threading.Dispatcher.Run(); 
}); 
addEditPairThread.IsBackground = true; 
addEditPairThread.Name = "AddEditPair"; 
addEditPairThread.SetApartmentState(ApartmentState.STA); 
addEditPairThread.Start(); 

這只是當我嘗試設置所有者的偉大工程的窗口,運行在主Ui線程上的窗口。

的例外,我得到的是:

The calling thread cannot access this object because a different thread owns it. 

我明白什麼錯誤意味着以及爲什麼會發生,所以後來我實現了以下內容:

// If invoke is not required - direct call 
if (addEditPair.Dispatcher.CheckAccess()) 
    method(); 
// Else if invoke is required - invoke 
else 
    addEditPair.Dispatcher.BeginInvoke(dispatcherPriority, method); 

,但我仍然得到同樣的錯誤。現在我很困惑!

任何想法的人?任何幫助,將不勝感激。

回答

-1

某種驗證似乎在窗口的Owner屬性上發生,它檢查窗口是否在同一個線程上創建。

我的解決辦法,以克服這一點,是爲了實現自己的類型主窗口的財產和存儲從構造到該參考,具體如下:

private MainWindow _main; 

public AddEditPair(MainWindow main) 
    : base(false) 
{ 
    InitializeComponent(); 

    // Initialise local variables 
    _main = main; 
} 

public MainWindow Main 
{ 
    get 
    { 
     return _main; 
    } 
} 

現在我有機會到主窗口。

+0

在這種情況下,您只能參考窗口。但是具有主動焦點的Windows邏輯將會丟失。 – Evgeny 2016-02-25 13:41:57

0

嘗試設置一個窗口是在另一個線程窗口的父母不可能在WPF沒有被凍結的窗口(這我不知道是可能的),因爲每個窗口都將無法訪問其他的數據。

是否有一個很好的理由,以創建單獨線程的窗口?大多數情況下,您應該很好地在相同的UI線程上創建窗口,並使用後臺工作來處理長時間運行的taks。

+0

AddEditPair窗口包含一個不斷更新的ListView - 每秒更新1到10次。這導致主UI線程凍結。在將其移至單獨的專用UI線程之後 - 一切都運行良好。 – c0D3l0g1c 2010-10-07 09:16:33

4

你爲什麼要在一個單獨的線程創建的窗口?

我假設你這樣做是因爲窗口執行長時間運行的代碼或需要被長時間運行的代碼訪問,如果這是真的,那麼當長時間運行的代碼正在運行時窗口將無響應(這是不好的甚至可以在某些情況下凍結整個系統,但我們暫時忽略這一點) - 並且您正在執行整個線程化操作,以在第二個窗口被凍結時保持主窗口響應。

這不支持WPF - 但即使它被支持,它也無法工作 - Windows鏈接相互「相關」的窗口消息隊列(這是保持行爲的唯一方法系統可預測),所以無響應窗口將不會處理消息,並且會阻塞隊列,從而阻止擁有者窗口接收消息,從而使消息不響應。

是有原因的大多數程序只有一個UI線程 - 的邏輯很簡單:

  • 運行在不同的線程的時候,唯一的理由是,如果你有兩個以上的長時間運行或阻塞操作你不希望他們互相阻止。

  • 執行長時間運行或阻止操作的窗口將無響應,將影響同一應用程序中的其他窗口(即使它們位於不同線程中)並可能會破壞整個系統的穩定性 - 所以我們不會想要那個。

  • 所以我不能執行阻塞或長期從一個窗口中運行操作,但使用一個後臺線程。

  • 如果一個窗口不執行長時間運行或阻塞操作,它將不會阻塞該線程,並且因此可以在沒有任何問題的情況下與其他表現良好的窗口在同一線程上運行。

  • 而且,由於同一線程上的窗口不會互相干擾,多線程增加了複雜性 - 所以沒有理由再多一個UI線程。

注意:只有實際顯示UI一個UI線程,這是完全正常的有使用WPF後臺線程永遠不會打開一個窗口(例如:在後臺創建一個大的固定文檔)我不叫那些UI線程,我也沒有說任何有關後臺線程的數量。

+0

有時WPF自己的測量/排列/渲染處理足以在慢速PC上的其他UI窗口中產生干擾行爲。 – 2013-07-19 19:15:27

1

如果你真的想在其他線程中設置擁有者,那麼你必須使用user32函數。

 [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()); 
      } 
} 

代碼獲取WPF處理程序:

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

注意窗口應多線程所有者調用之前被初始化!

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(); 

P.s.也可以使用SetParent:

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