2009-11-22 14 views
7

我在構造函數中初始化屬性時遇到了控件模板內數據綁定問題。如果屬性在構造函數中初始化,XAML綁定似乎不會設置

這裏是展示情況(你也可以下載sample solution):

CustomControl1.cs

public class CustomControl1 : ContentControl 
{ 
    static CustomControl1() 
    { 
     DefaultStyleKeyProperty.OverrideMetadata(
      typeof(CustomControl1), 
      new FrameworkPropertyMetadata(typeof(CustomControl1))); 
    } 

    public CustomControl1() 
    { 
     Content = "Initial"; // comment this line out and everything 
          // will start working just great 
    } 
} 

CustomControl1風格:

<Style TargetType="{x:Type local:CustomControl1}"> 
    <Setter Property="Template"> 
     <Setter.Value> 
      <ControlTemplate TargetType="{x:Type local:CustomControl1}"> 
       <Border Background="{TemplateBinding Background}" 
         BorderBrush="{TemplateBinding BorderBrush}" 
         BorderThickness="{TemplateBinding BorderThickness}"> 
        <ContentPresenter /> 
       </Border> 
      </ControlTemplate> 
     </Setter.Value> 
    </Setter> 
</Style> 

CustomControl2.cs:

public class CustomControl2 : ContentControl 
{ 
    static CustomControl2() 
    { 
     DefaultStyleKeyProperty.OverrideMetadata(
      typeof(CustomControl2), 
      new FrameworkPropertyMetadata(typeof(CustomControl2))); 
    } 
} 

CustomControl風格:

<Style TargetType="{x:Type local:CustomControl2}"> 
    <Setter Property="Template"> 
     <Setter.Value> 
      <ControlTemplate TargetType="{x:Type local:CustomControl2}"> 
       <Border Background="{TemplateBinding Background}" 
         BorderBrush="{TemplateBinding BorderBrush}" 
         BorderThickness="{TemplateBinding BorderThickness}"> 
        <local:CustomControl1 
         Content="{Binding Content, 
          RelativeSource={RelativeSource 
            AncestorType=local:CustomControl2}}" /> 
       </Border> 
      </ControlTemplate> 
     </Setter.Value> 
    </Setter> 
</Style> 

Window1.xaml:

<Window x:Class="WpfApplication5.Window1" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     Title="Window1" Height="300" Width="300" 
     xmlns:local="clr-namespace:WpfApplication5"> 
    <Grid> 
     <local:CustomControl2 Content="Some content" /> 
    </Grid> 
</Window> 

所以,問題是:當你啓動應用程序,這是由設置CustomControl1的內容似乎是 「初始」構造函數,而不是「Some content」字符串,它應該通過綁定來設置。

當我們從構造函數中刪除初始化時,綁定開始工作。首先,讓我預測答案:「您應該在其元數據中設置依賴項屬性的初始值:無論是在註冊時還是通過元數據重寫功能」。 Yeap,你說得對,但這種初始化方法的問題在於屬性是集合類型的,所以如果我提供new MyCustomCollection()作爲屬性的默認值,那麼CustomControl1的每個實例將共享該集合的相同實例這顯然不是主意。

我已經做了的問題的一些調試,這裏的結果:

  • 結合實例被創建,當我們把它放在元素的語法和分配x:Name它,那麼它是通過Template.FindName("PART_Binding", this)訪問裏面有OnApplyTemplate
  • 綁定只是沒有設置屬性:在同一OnApplyTemplate代碼this.GetBindingExpression(ContentProperty)返回null
  • 綁定本身沒有任何問題:在OnApplyTemplate裏面我們可以查看它,然後我們可以簡單地將它設置爲這樣的屬性:this.SetBinding(ContentProperty, myBinding) - 一切都會正常工作。

任何人都可以解釋如何以及爲什麼發生?

有沒有人有解決方案設置依賴項屬性的非共享初始值,所以綁定不會中斷?

在此先感謝!

UPD: 最奇怪的是,與最高跟蹤級別調試輸出是這兩種情況下是相同的:要麼當沒有發生,或者如果它的約束力。

這裏有雲:

System.Windows.Data Warning: 52 : Created BindingExpression (hash=18961937) for Binding (hash=44419000) 
System.Windows.Data Warning: 54 : Path: 'Content' 
System.Windows.Data Warning: 56 : BindingExpression (hash=18961937): Default mode resolved to OneWay 
System.Windows.Data Warning: 57 : BindingExpression (hash=18961937): Default update trigger resolved to PropertyChanged 
System.Windows.Data Warning: 58 : BindingExpression (hash=18961937): Attach to WpfApplication5.CustomControl1.Content (hash=47980820) 
System.Windows.Data Warning: 62 : BindingExpression (hash=18961937): RelativeSource (FindAncestor) requires tree context 
System.Windows.Data Warning: 61 : BindingExpression (hash=18961937): Resolve source deferred 
System.Windows.Data Warning: 63 : BindingExpression (hash=18961937): Resolving source 
System.Windows.Data Warning: 66 : BindingExpression (hash=18961937): Found data context element: <null> (OK) 
System.Windows.Data Warning: 69 :  Lookup ancestor of type CustomControl2: queried Border (hash=11653293) 
System.Windows.Data Warning: 69 :  Lookup ancestor of type CustomControl2: queried CustomControl2 (hash=54636159) 
System.Windows.Data Warning: 68 : RelativeSource.FindAncestor found CustomControl2 (hash=54636159) 
System.Windows.Data Warning: 74 : BindingExpression (hash=18961937): Activate with root item CustomControl2 (hash=54636159) 
System.Windows.Data Warning: 104 : BindingExpression (hash=18961937): At level 0 - for CustomControl2.Content found accessor DependencyProperty(Content) 
System.Windows.Data Warning: 100 : BindingExpression (hash=18961937): Replace item at level 0 with CustomControl2 (hash=54636159), using accessor DependencyProperty(Content) 
System.Windows.Data Warning: 97 : BindingExpression (hash=18961937): GetValue at level 0 from CustomControl2 (hash=54636159) using DependencyProperty(Content): 'Some content' 
System.Windows.Data Warning: 76 : BindingExpression (hash=18961937): TransferValue - got raw value 'Some content' 
System.Windows.Data Warning: 85 : BindingExpression (hash=18961937): TransferValue - using final value 'Some content' 

UPD2:增加了一個鏈接到sample solution

+1

由於還有第二個答案(第一個被作者刪除),他們甚至沒有遠程匹配問題,所以我用粗體突出顯示了我的問題。請確保你得到**問題**必需品,並確保你正在回答**問題**。 – archimed7592

回答

3

我在MSDN論壇上發現了這個問題,有人建議在Microsft Connect上創建an issue ......長話短說:我沒有清楚理解的關鍵機制是DP的值優先級。它完全描述爲here(本地值的優先級高於模板父級的值)。

其次,不是真正明顯的時刻,如果它被設置爲任何模板(甚至不是元素自己的模板),該值被認爲是模板父級的值。

HTH。

+1

依賴項屬性值優先級的更新鏈接:https://msdn.microsoft.com/library/ms743230(v=vs.100).aspx – Eric

1

也許你應該使用雙向結合模式?你應該控制「某些內容」嗎?由於綁定是OneWay,因此無法將其存儲在控件的模型中。您的案例綁定中的 會發現模型屬性中存在值,並會覆蓋「某些內容」。如果不初始化屬性,綁定什麼也不做,因爲它忽略空值並且你看到「有些內容」。 我希望我的解釋清楚。

編輯

對不起,你的問題有點誤解。我已經下載了您的演示應用程序並轉載了該問題。 閱讀thisthis MSDN文章顯示您的意圖是正確的。但是你可以發現有這樣的話:

下面的虛方法或回調的SetValue調用設置依賴屬性值的計算過程中可能稱爲:ValidateValueCallback,PropertyChangedCallback,CoerceValueCallback,OnPropertyChanged。

因此,在構造函數中設置DependencyProperty的值可能與調用未構造的對象的虛方法一樣危險。

好的,在構造函數中設置DependencyProperty是不好的。我的下一個想法是在一些回調中設置值(我使用了OnInitialized,因爲它應該在Control的構造函數後面調用)。我發現了另一個非常奇怪的行爲。如果我不設置構造函數(這樣)

public CustomControl1() 
    { 
     //Content = "Initial1"; 
    } 
    protected override void OnInitialized(EventArgs e) 
    { 
     Content = "Initial2"; 
     var check = Content; // after this check == "Initial_2" 
    } 

任何價值,我不看「Initial2」窗口,即使我沒有在Window1.xaml內容指定任何價值。請注意,該值設置正確(因爲我看到檢查它)。 但是,如果我取消註釋Content =「Initial1」;字符串,我看到「Initial2」。另外,如果我初始化內容在OnInitialized綁定工作正常,但它不能解決內容的實際值是「Initial2」。看起來像它的來源不是內容屬性。

以後我會繼續解決這個問題。 我希望這些信息對您有所幫助。

+0

事實上,當我碰到這個問題時,它是在一個雙向綁定的情況下。在這個例子中,我刪除了所有無關緊要的東西。所以,沒有TwoWay不能使它工作。無論如何,即使它具有:單向綁定應該以這樣的方式工作,即每當我更改源時,應該更新目標,無論是否存在任何重寫的值。但問題不在於一次性綁定失敗,而在於綁定本身並不屬於該屬性。彷彿根本沒有約束力。 – archimed7592

+0

一些關於樣本醜陋設計的話題。請不要看所提供代碼的不適用性,它僅用於演示目的。這個問題出現在一個巨大的項目中,對於綁定模式或控件對內容的處理都有完美的意義。我希望很明顯,該樣本旨在幫助重現問題。 – archimed7592

相關問題