自定义ExtendedCalendar控件未检测ItemsSource项属性变更问题
问题场景
扩展WPF Calendar实现了ExtendedCalendar控件,添加了ItemsSource依赖属性(类型IEnumerable<CalendarDay>),用于给日历按钮显示ToolTip并通过指定颜色高亮。CalendarDay类已实现INotifyPropertyChanged接口。
当ViewModel中DefinedDates(ObservableCollection<CalendarDay>类型)添加新项时,控件能触发Refresh();但更新集合中已有项的Description或Color属性时,控件无法检测到变更,不会触发Refresh()。
相关代码
CalendarDay.cs
public class CalendarDay : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; private void OnPropertyChanged(string propertyName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } private DateTime date; public DateTime Date { get { return date; } set { if (date == value) return; date = value; OnPropertyChanged("Date"); } } private string description; public string Description { get { return description; } set { if (description == value) return; description = value; OnPropertyChanged("Description"); } } private string color; public string Color { get { return color; } set { if (color == value) return; color = value; OnPropertyChanged("Color"); } } public CalendarDay() { } }
ExtendedCalendar.cs(ItemsSource相关实现)
public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register( "ItemsSource", typeof(IEnumerable<CalendarDay>), typeof(ExtendedCalendar), new PropertyMetadata(new PropertyChangedCallback(OnItemsSourcePropertyChanged)) ); private static void OnItemsSourcePropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) { if (sender is ExtendedCalendar control) control.OnItemsSourceChanged((IEnumerable<CalendarDay>)e.OldValue, (IEnumerable<CalendarDay>)e.NewValue); } private void OnItemsSourceChanged(IEnumerable<CalendarDay> oldValue, IEnumerable<CalendarDay> newValue) { if (oldValue is INotifyCollectionChanged oldValueINotifyCollectionChanged) { oldValueINotifyCollectionChanged.CollectionChanged -= new NotifyCollectionChangedEventHandler(NewValueINotifyCollectionChanged_CollectionChanged); } if (newValue is INotifyCollectionChanged newValueINotifyCollectionChanged) { newValueINotifyCollectionChanged.CollectionChanged += new NotifyCollectionChangedEventHandler(NewValueINotifyCollectionChanged_CollectionChanged); } } void NewValueINotifyCollectionChanged_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { Refresh(); }
解决方法
当前代码仅监听了集合的增删变更,未监听集合中单个元素的属性变更。需要为每个CalendarDay对象的PropertyChanged事件注册监听,在属性变更时触发Refresh()。
修改ExtendedCalendar的相关实现代码如下:
public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register( "ItemsSource", typeof(IEnumerable<CalendarDay>), typeof(ExtendedCalendar), new PropertyMetadata(new PropertyChangedCallback(OnItemsSourcePropertyChanged)) ); private static void OnItemsSourcePropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) { if (sender is ExtendedCalendar control) control.OnItemsSourceChanged((IEnumerable<CalendarDay>)e.OldValue, (IEnumerable<CalendarDay>)e.NewValue); } private void OnItemsSourceChanged(IEnumerable<CalendarDay> oldValue, IEnumerable<CalendarDay> newValue) { // 移除旧集合的监听 if (oldValue is INotifyCollectionChanged oldCollection) { oldCollection.CollectionChanged -= OnCollectionChanged; UnsubscribeItems(oldValue); } // 添加新集合的监听 if (newValue is INotifyCollectionChanged newCollection) { newCollection.CollectionChanged += OnCollectionChanged; SubscribeItems(newValue); } } private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { // 处理新增项的监听注册 if (e.NewItems != null) { foreach (CalendarDay item in e.NewItems) { item.PropertyChanged += OnCalendarDayPropertyChanged; } } // 处理移除项的监听注销 if (e.OldItems != null) { foreach (CalendarDay item in e.OldItems) { item.PropertyChanged -= OnCalendarDayPropertyChanged; } } Refresh(); } private void OnCalendarDayPropertyChanged(object sender, PropertyChangedEventArgs e) { // 仅在目标属性变更时触发刷新,也可直接无条件刷新 if (e.PropertyName == nameof(CalendarDay.Description) || e.PropertyName == nameof(CalendarDay.Color)) { Refresh(); } } // 批量注册元素的PropertyChanged监听 private void SubscribeItems(IEnumerable<CalendarDay> items) { if (items == null) return; foreach (var item in items) { item.PropertyChanged += OnCalendarDayPropertyChanged; } } // 批量注销元素的PropertyChanged监听 private void UnsubscribeItems(IEnumerable<CalendarDay> items) { if (items == null) return; foreach (var item in items) { item.PropertyChanged -= OnCalendarDayPropertyChanged; } }
说明
- 新增
SubscribeItems和UnsubscribeItems方法,批量处理集合元素的PropertyChanged事件注册/注销 - 在集合变更时,对新增/移除的元素单独处理监听,避免遗漏
- 新增
OnCalendarDayPropertyChanged方法,当元素的Description或Color属性变更时触发控件刷新,确保UI及时更新
内容的提问来源于stack exchange,提问作者James0n
相关产品推荐
相关产品推荐

