2012-10-15 74 views
3

我在開發WPF應用程序時遇到了問題。 該應用程序基於Prism。 在加載任何Window之前,應用程序使用棱鏡引導程序和 進行引導,應用程序會在不同的線程(STA)上打開模式對話框,然後加載一堆內容(服務等) 對話框在期間打開並且允許通知用戶應用程序啓動過程的進度(使用事件聚合器傳遞更新)。 加載完成後,bootstraper關閉對話框並打開主應用程序窗口。 到目前爲止這麼好... 然後當關閉應用程序時,同樣的事情正在進行。 關閉主窗口,打開一個對話框(再次在新的STA線程上),以允許通知。 但現在,當觸擊ShowDialog調用(發生在新STA線程內)時, 會引發異常: 「不能使用屬於與其父級Freezable不同的線程的DependencyObject」。 經過長時間的調試,我發現異常的原因是從應用程序級的合併字典(在wpf UI線程上實例化)中獲取的畫筆/圖像的背景。 如果沒有ResouceDictionary加載圖像 - 一切順利。無法使用屬於與其父項不同的線程的DependencyObject - 棱鏡


要總結: 使用ResourceDictionary中,並僅在第二呼叫到一個新的STA線程,其依次加載了一個對話框,並調用ShowDialog的時候正好拋出一個異常時,異常僅觀察 如果您只有一個對話框(例如,啓動時沒有對話框,只有關閉過程中的對話框),則不會發生異常。


我的問題則是:究竟是什麼原因呢?在這種情況下,這個例外意味着什麼?我知道一般情況下,其他線程有一些UI線程更新,但我不明白爲什麼這隻發生在dialgo +線程的第二個實例上。

謝謝:)

回答

0

我也有類似的問題。我不確定你是如何實施背景的。我可以試着解釋我的情況,也許你可以從中得到一些東西。我創建了我自己的基本窗口,我們稱之爲MyWindow,它繼承自Window。 IE瀏覽器。

public class MyWindow : Window 
{ 
} 

我想要的是應用來自應用程序資源字典的動態資源的背景。

我第一次去這個答案

public class MyWindow : Window 
    { 
     public MyWindow() 
     { 
      this.SetResourceReference(BackgroundProperty, "MyResourceKey"); 
     } 
    } 

現在這個工作對我來說,當資源是一套顏色即。

<SolidColorBrush x:Key="MyResourceKey" Color="White"/> 

我發現,當我設置的資源引用到系統的顏色,我會得到你所得到的問題。

<SolidColorBrush x:Key="MyResourceKey" Color="{DynamicResource {x:Static SystemColors.WindowColorKey}}"/>  

這將工作的第一次,但第二次我會得到父母凍結錯誤。所以我最初的想法是,這是一個線程問題,我只需要調用調度程序。現在,這是我陷入困境的地方,因爲我認爲我需要在窗口上調用它。不對。您需要在該資源的依賴對象上調用它。
問題。您沒有使用SetResourceReference的對象,因爲它查找資源並創建對其的引用。所以我們需要的是實際的依賴對象。要從資源中獲取對象,您可以執行此操作。

object temp = this.TryFindResource("MyResourceKey"); 

現在你有了這個對象,但是這需要是一個依賴對象。我沒有嘗試過,但你可能只是可以做到這一點

DependencyObject temp = (DependencyObjet)this.TryFindResource("MyResourceKey"); 

現在你有了依賴對象!這是導致我們的可凍結父項的線程問題的原因。現在我們調用這個對象的調度器。我最終結束了這樣的事情。這對我有效,但我可能會嘗試清理它一點。

public class MyWindow: Window 
    { 
     public MyWindow() 
     { 
      SetResources();     
     } 

     private void SetResources() 
     { 
      DependencyObject dependencyObject; 
      object temp; 

       temp = this.TryFindResource("MyResourceKey"); 

       if (temp != null) 
       { 
        if (temp is DependencyObject) 
        { 
         dependencyObject = (DependencyObject)temp; 
         if (!dependencyObject.CheckAccess()) 
         { 
          dependencyObject.Dispatcher.BeginInvoke(new System.Action(() => { this.SetResources(); })); 
         } 
         else 
         { 
          this.SetValue(BackgroundProperty, temp);        
         } 
        }     
       } 
      }     
    } 

現在,這只是設置背景屬性。我相信這應該適用於一種風格。所以你可以做

this.SetValue(StyleProperty, temp) 

花了一點點弄明白。但一旦我得到它的工作,我感到振奮。它看起來像我們的資源正在使用的依賴項對象遇到了線程問題,因此它是第一次加載,但不是第二次加載。它第一次在正確的線程上,但在另一個線程的某個地方出現了。但要弄清楚這一點。如果有人有更好的解決方案,我很樂意看到它。

1

正如你提到的正確,你的背景對象是在主UI線程創建的。你的背景實際上是一個Brush對象,Brush是一個DependencyObject。

當DependencyObject的創建它「依賴」於STA線程上創建它的。與其他依賴對象一樣,它只能在自己的線程上使用。這意味着STA和舊COM對象的兼容性模型。

因此,當您嘗試使用其他STA線程上你會得到一個適當的異常。

P.S我有一個已定義爲資源的圖像同樣的問題。

+0

所以它仍然發生除去第一對話框時(在關機對話框仍然沒有自己的資源 – benchuk

+0

。「刷是一種DependencyObject的。」 - 你救了我的命! – user3162662