You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自定义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;
    }
}

说明

  1. 新增SubscribeItems和UnsubscribeItems方法,批量处理集合元素的PropertyChanged事件注册/注销
  2. 在集合变更时,对新增/移除的元素单独处理监听,避免遗漏
  3. 新增OnCalendarDayPropertyChanged方法,当元素的Description或Color属性变更时触发控件刷新,确保UI及时更新

内容的提问来源于stack exchange,提问作者James0n

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 19:25:19