2015-06-15 48 views
8
void itemCommand_Click(Office.CommandBarButton Ctrl, ref bool CancelDefault) 
{ 
    var thread = new Thread(() => 
    { 
    if (LoginCheck()) 
    { 
     ItemWindow itw = new ItemWindow(); 
     //Dispatcher.CurrentDispatcher.Invoke((System.Action)(() => 
     //{ 
       itw.Show(); 
       itw.Closed += (sender2, e2) => { itw.Dispatcher.InvokeShutdown(); }; 
     //})); 

     Dispatcher.Run(); 
    } 
    }); 

    thread.SetApartmentState(ApartmentState.STA); 
    thread.Start(); 

} 

我不斷收到錯誤消息「調用線程無法訪問此對象,因爲不同的線程擁有它」。在線「itw.show();」當這個函數調用兩次時。對於第一次調用它可以正常工作,並且在窗口關閉並嘗試再次打開後,它會失敗。正如我註釋了「Invoke」方法,它也不適用於Dispatcher。請幫我找到解決方案。 謝謝。WPF新窗口創建新線程錯誤

-----------------編輯

爲什麼我創建一個新的線程,是因爲它是一個Excel插件的原因。如果我從主線程創建它們,我無法從主線程創建窗口,這是與Windows發生衝突的Excel。
我不明白的是,爲什麼來自新線程的新實例(ItemWindow)與舊線程相沖突。

+2

爲什麼要創建一個新線程來顯示此窗口?這通常不是一個好主意。其次,我已經嘗試過了,它工作正常(除非我不知道「LoginCheck」是什麼)。例外發生在哪裏? –

+2

所有與UI相關的代碼都必須在主線程中運行。你想在這裏做什麼? – almulo

+0

通常,此錯誤是指您嘗試訪問您已從第一個表單中刪除的第一個表單上的屬性。我們實際上必須看到導致問題的第二種形式會發生什麼。 – DoomVroom

回答

0

我在一個新的應用程序中創建了一個簡單的測試方法,當我單擊我的主窗體上的(僅)按鈕時被調用。該方法是這樣的:

private void Button_Click(object sender, RoutedEventArgs e) 
{ 
    Thread thread = new Thread(() => 
    { 
     Window1 window = new Window1(); 
     window.Closed += (s, a) => window.Dispatcher.InvokeShutdown(); 
     window.Show(); 
     System.Windows.Threading.Dispatcher.Run(); 
    }); 

    thread.SetApartmentState(ApartmentState.STA); 
    thread.Start(); 
} 

Window1是一個窗口類我做的,沒有什麼,但對一個單一TextBlock。我可以根據需要多次單擊該按鈕,並繼續打開沒有問題的新窗口(不管我是否先關閉前一個窗口)。

我懷疑問題發生在代碼中,您沒有向我們展示某處。你需要非常小心,新線程上的任何內容都不會嘗試訪問與主線程相關的任何UI。運行在不同線程上的Windows不能相互通信,除非它們通過另一個線程的調度程序。當從一個非創建對象的線程訪問DispatcherObject的任何方法或屬性時,您看到的異常被拋出。

退一步,爲什麼新窗口在自己的線程上很重要?除非新窗口將獨佔該線程,否則它可能會在主線程上正常運行。如果你正在運行一些長時間的阻塞操作,也許這個操作本身應該移動到一個線程而不是整個窗口。我不知道你在做什麼,但是這是需要思考的。


編輯:意識到你可能無法在一個典型的WPF應用程序在運行(看起來像你可能會在Office插件),我更新了我的測試,以啓動Windows自己的線程完全獨​​立的。不過,我仍然可以連續發佈兩個窗口,沒有問題。

這是我的新測試。這種方法和測試類Window1是我的應用程序的全部。

[STAThread] 
public static int Main(string[] args) 
{ 
    ThreadStart threadFunc =() => 
    { 
     Window1 window = new Window1(); 
     window.Closed += (s, a) => window.Dispatcher.InvokeShutdown(); 
     window.Show(); 
     System.Windows.Threading.Dispatcher.Run(); 
    }; 

    Thread thread = new Thread(threadFunc); 
    thread.SetApartmentState(ApartmentState.STA); 
    thread.Start(); 
    thread.Join(); 

    thread = new Thread(threadFunc); 
    thread.SetApartmentState(ApartmentState.STA); 
    thread.Start(); 
    thread.Join(); 

    return 0; 
} 

所以,你試圖做什麼似乎沒有什麼內在錯誤,也沒有看到你的代碼中有任何明顯的問題。我懷疑在自定義窗口中顯示的某處出現了一些無效的跨線程通信。 (或者,或者您遇到Office插件特有的問題。)

+0

我認爲這與Excel插件有關。我不明白的是爲什麼新線程的新實例(ItemWindow)與舊線程相沖突。 – icewall

+0

你看了看異常的調用堆棧嗎?也許它會揭示一些關於造成無效呼叫的線索。此外,您可以關閉「Just My Code」調試器選項,以在Visual Studio的「調用堆棧」窗口中查看完整的調用堆棧(包括來自外部程序集的方法)。 – Xavier

0

您正在嘗試將事件處理程序連接到ItemWindow 它已經可見。

您需要從切換順序:

ItemWindow itw = new ItemWindow(); 
itw.Show(); 
itw.Closed += (sender2, e2) => { itw.Dispatcher.InvokeShutdown(); }; 

ItemWindow itw = new ItemWindow(); 
itw.Closed += (sender2, e2) => { itw.Dispatcher.InvokeShutdown(); }; 
itw.Show(); 
-1

一個可能的原因是依賴屬性。涉及到線程時,依賴屬性有點挑剔。

即使你沒有定義你自己的DepProps,你的窗口仍然會有一些,並且沒有辦法擺脫它們。

DepProps有一個很大的缺點:它們是線程綁定的,不能從另一個線程訪問。初始化DepProps的線程定義了哪個線程擁有所有權限,在您的情況下,首先調用new ItemWindow()。在第一次調用之後,您的線程已設置,並且您需要該線程來訪問您的DepProps。

對於第一個窗口沒有問題,但第二個窗口顯然有不同的線程。我不確切知道DepProps是如何做到這一點的,但您可以嘗試捕獲並恢復第一個線程的同步上下文。另一個選擇是捕獲第一個線程的調度程序(不是主線程)