2011-04-26 101 views
9

我有一個用戶控件,它公開了一個名爲VisibileItems的DependencyProperty 每當該屬性得到更新時,我需要觸發另一個事件。 爲了達到這個目的,我用PropertyChangedCallback事件添加了一個FrameworkPropertyMetadata。WPF:PropertyChangedCallback只觸發一次

由於某些原因,此事件只被調用一次,並且在下次VisibleItems更改時不會觸發。

XAML:

<cc:MyFilterList VisibleItems="{Binding CurrentTables}" /> 

CurrentTables是MyViewModel一個DependencyProperty。當前表經常變化。我可以將另一個WPF控件綁定到CurrentTables,並且看到UI中的更改。

下面是我通過步入VisiblePropertyChanged有線VisibleItems與PropertyChangedCallback

public static readonly DependencyProperty VisibleItemsProperty = 
    DependencyProperty.Register(
    "VisibleItems", 
    typeof(IList), 
    typeof(MyFilterList), 
    new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender, new PropertyChangedCallback(VisiblePropertyChanged)) 

    ); 

public IList VisibleItems { 
    get { return (IList)GetValue(VisibleItemsProperty); } 
    set { SetValue(VisibleItemsProperty, value); } 
} 

的方式,我可以看到它被觸發第一次CurrentTables被設定。但不是隨後的時間。

UPDATE

因爲你們中的一些質疑CurrentTables被修改的方式,它完全在變化重新分配:

OnDBChange()... 
CurrentTables = new List<string>(MainDatabaseDataAdapter.GetTables(this.SelectedServer, this.SelectedDatabase)); 

此行被調用的每一個變化,但我VisiblePropertyChanged處理程序被調用只有第一次。

UPDATE

,如果我直接分配VisibleItems,處理程序是否被調用每一次!

TestFilterList.VisibleItems = new List<string>(Enumerable.Range(1, DateTime.Now.Second).ToList().Select(s => s.ToString()).ToList()); 

所以,它看起來像這個問題從的DependencyProperty(VisibleItems)莖看其他的DependencyProperty(CurrentTables)。不知何故,綁定工作在第一次財產變動,但不是在後續的變化? 正如你們中的一些人所建議的,試圖用snoop來檢查這個問題。

+0

你確定綁定保持不變?我將使用Snoop來驗證綁定是否正確更新VisibleItems屬性。 – 2011-04-26 20:57:30

+0

你暗示但不是明確的:你確定'VisibleItems'實際上改變到一個不同的值,沒有改變屬性被調用的回調? – 2011-04-26 21:13:22

+0

裏克,是的,我相信,我可以通過它看到它被調用,現在我在每次調用時創建一個「新列表」,以確保它不是同一個實例。而且如果您查看代碼,您可以看到它每次都從數據庫獲取列表。 – 2011-04-26 22:23:45

回答

13

你設定一個「局部」的價值也有一個依賴屬性(即直接分配到依賴屬性的setter) OneWay對它有約束力嗎?如果是的話,設置本地值將取消綁定,在the MSDN dependency property overview:

綁定提到被視爲本地值,這意味着如果您設置另一個本地值,你將消除綁定。

當它被要求在依賴項屬性上存儲本地值時,依賴屬性機制沒有多少功能。它不能通過綁定發送值,因爲綁定「指向」錯誤的方式。在設置爲本地值後,它不再顯示從綁定中獲得的值。由於它不再顯示綁定的值,因此它將刪除綁定。

一旦綁定消失,當綁定的源屬性更改其值時,將不再調用PropertyChangedCallback。這可能是回調沒有被調用的原因。

如果將綁定設置爲TwoWay,則綁定系統確實在某處存儲您設置的'local'值:綁定的源屬性。在這種情況下,不需要消除綁定,因爲依賴項屬性機制可以將值存儲在源屬性中。

因爲發生以下情況,這種情況不會導致堆棧溢出:

  • 依賴屬性接收「本地」值。
  • 依賴屬性機構沿着結合到源屬性發送值「向後」,
  • 源屬性設置屬性值和火災PropertyChanged
  • 依賴屬性機構接收PropertyChanged事件,檢查源屬性的新值,發現它不是招沒有改變,也沒有做進一步的事情。

這裏的關鍵點是,如果你開除一個PropertyChanged事件屬性其價值並沒有改變,在綁定到您的財產依賴屬性的任何PropertyChangedCallback旨意不會被調用。

爲簡單起見,我在上面忽略了IValueConverter s。如果您確實有轉換器,請確保它正確地在兩個方向上轉換值。我還假定另一端的屬性是實現INotifyPropertyChanged的對象上的視圖模型屬性。在綁定的源端可能有另一個依賴項屬性。依賴屬性機制也可以處理。

恰巧,WPF(和Silverlight)不包含堆棧溢出檢測。如果在PropertyChangedCallback中,將依賴項屬性的值設置爲與其新值不同(例如,通過遞增整數值屬性或將字符串附加到字符串值屬性),則會發生堆棧溢出。

+0

不,不要在事件中設置VisibleItems。該事件執行翻譯並在我的用戶控件上設置另一個屬性。 – 2011-04-26 22:26:27

+0

@魯克伍德:千次感謝!你的帖子至少讓我至少有一天的時間在調試。 – 2012-06-14 13:07:00

0

您可能會遇到集合內容發生變化但不是實際實例的問題。在這種情況下,你需要使用一個ObservableCollection,做這樣的事情:

public static readonly DependencyProperty VisibleItemsProperty = 
    DependencyProperty.Register(
    "VisibleItems", 
    typeof(IList), 
    typeof(MyFilterList), 
    new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender, new PropertyChangedCallback(VisibleItemsChanged))); 

    private static void VisibleItemsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
    { 
     var myList = d as MyFilterList; 
     if (myList == null) return; 

     myList.OnVisibleItemsChanged(e.NewValue as IList, e.OldValue as IList); 
    } 

    protected virtual void OnVisibleItemsChanged(IList newValue, IList oldValue) 
    { 
     var oldCollection = oldValue as INotifyCollectionChanged; 
     if (oldCollection != null) 
     { 
      oldCollection.CollectionChanged -= VisibleItems_CollectionChanged; 
     } 
     var newCollection = newValue as INotifyCollectionChanged; 
     if (newCollection != null) 
     { 
      newCollection.CollectionChanged += VisibleItems_CollectionChanged; 
     } 
    } 
+0

集合正在被重新分配。 – 2011-04-26 20:47:59

+1

你能在你的原始問題中發表一些更多代碼嗎? – bendewey 2011-04-26 20:52:33

+0

更多代碼,補充。謝謝 – 2011-04-26 20:59:17

1

如果你只是實例化一個MyFilterList,並通過代碼設置VisibleItems這樣的:

var control = new MyFilterList(); 
control.VisibleItems = new List<string>(); 
control.VisibleItems = new List<string>(); 

你可能會看到的PropertyChangedCallback發生的每一次。意思是,問題在於綁定,而不是回調。確保你沒有綁定錯誤,你正在籌集PropertyChanged,並且你沒有破壞綁定(例如通過在代碼中設置VisibleItems

+0

對。我確實直接分配了這個屬性,並且它每次都工作..所以這個問題似乎源於一個dep屬性看另一個..不知怎的,通知變化只發生一次...... – 2011-04-27 14:57:32

4

我在我的代碼中有同樣的問題,盧克是正確的。我在PropertyChangedCallback中通過mystake調用SetValue,導致潛在的無限循環。 WPF默默阻止這種禁用回調!

我的WPF用戶控件是

PatchRenderer 

我的C#屬性注:

[Description("Note displayed with star icons"), 
    Category("Data"), 
    Browsable(true), 
    EditorBrowsable(EditorBrowsableState.Always), 
    DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)] 
    public int Note 
    { 
     get { return (int)GetValue(NoteProperty); } 
     set { SetValue(NoteProperty, value); /* don't put anything more here */ } 
    } 

我的WPF屬性

public static readonly DependencyProperty 
     NoteProperty = DependencyProperty.Register("Note", 
     typeof(int), typeof(PatchRenderer), 
     new PropertyMetadata(
      new PropertyChangedCallback(PatchRenderer.onNoteChanged) 
      )); 

    private static void onNoteChanged(DependencyObject d, 
       DependencyPropertyChangedEventArgs e) 
    { 
     // this is the bug: calling the setter in the callback 
     //((PatchRenderer)d).Note = (int)e.NewValue; 

     // the following was wrongly placed in the Note setter. 
     // it make sence to put it here. 
     // this method is intended to display stars icons 
     // to represent the Note 
     ((PatchRenderer)d).UpdateNoteIcons(); 
    }