WPF:只读集合在 XAML 中的绑定(WPF:Binding for readonly collection in xaml)

问题背景:

某一天,我想做一个签到打卡的日历。基于 Calendar,想实现这个目标,于是找到了它的 SelectedDates 属性,用于标记签到过的日期。

那么 问题 来了:

基于MVVM模式,想将其在xaml中绑定到ViewModel中的一个ObservableCollection<DateTime>属性。但 SelectedDates 只读 CLR 属性。

解决思路:

给它搭个桥:创建一个相同Type的附加属性,用附加属性绑定到ViewModel中的ObservableCollection<DateTime>属性。并在附加属性的变更通知和集合变更通知中添加处理:进行操作的桥接,将集合对象、集合的变动传递到SelectedDates中即可

主要代码如下:(PS:初步使用,如果有其他问题请指教:e-mal:yangqi1990917@163.com)

public static class CalendarEx
    {
        #region SelectedDates 只读,不可在 Xaml 中绑定的问题解决方案:建立附加属性通道

        public static ObservableCollection<DateTime> GetSelectedDatesSource(DependencyObject obj)
        {
            return (ObservableCollection<DateTime>)obj.GetValue(SelectedDatesSourceProperty);
        }

        public static void SetSelectedDatesSource(DependencyObject obj, ObservableCollection<DateTime> value)
        {
            obj.SetValue(SelectedDatesSourceProperty, value);
        }

        public static readonly DependencyProperty SelectedDatesSourceProperty =
            DependencyProperty.RegisterAttached("SelectedDatesSource", typeof(ObservableCollection<DateTime>), typeof(CalendarEx), new PropertyMetadata(null, SetSelectedDatesSourcePropertyChangedCallback));


        /// <summary>
        ///  REMARK 只读集合的 XAML 绑定(目前是单向绑定,双向绑定需定义 SelectedDates 的变更事件)
        /// </summary>
        /// <param name="d"></param>
        /// <param name="args"></param>
        private static void SetSelectedDatesSourcePropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs args)
        {
            // 附加属性应用于 Calendar
            Calendar calendar = d as Calendar;
            if (calendar == null) return;

            // 对象本身未变更,则不进行任何操作,避免相同对象多次添加变更事件
            if (args.NewValue == args.OldValue) return;

            // 变更 SelectedDatesSource 对象,先获取新集合对象的集合内容
            ObservableCollection<DateTime> newValue = args.NewValue as ObservableCollection<DateTime>;
            if (newValue == null) return;
            
            // 应用新内容
            calendar.SelectedDates.Clear();
            foreach (DateTime time in newValue)
            {
                calendar.SelectedDates.Add(time);
            }

            // 获取 SelectedDatesSource 对象,添加集合变更通知处理,在其中处理目标集合对象
            ObservableCollection<DateTime> sourceCollection = GetSelectedDatesSource(d);
            if (sourceCollection == null) return;

            // _NOTE 这里使用 local 函数的目的是,集合变更通知函数(静态)需要访问 calendar,使用 local/匿名 函数方便访问,否则需要定义一个根据集合访问 calendar 的服务
            // 另外,local 函数比匿名函数好的地方在于,可以取消事件订阅(经验证)
            void SelectedDatesOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs cArgs)
            {
                ObservableCollection<DateTime> collection = sender as ObservableCollection<DateTime>;
                if (collection == null) return;

                switch (cArgs.Action)
                {
                    case NotifyCollectionChangedAction.Add:
                        foreach (DateTime item in cArgs.NewItems)
                        {
                            calendar.SelectedDates.Add(item);
                        }
                        break;
                    case NotifyCollectionChangedAction.Remove:
                        foreach (DateTime item in cArgs.NewItems)
                        {
                            calendar.SelectedDates.Remove(item);
                        }
                        break;
                    case NotifyCollectionChangedAction.Replace:
                        foreach (DateTime item in cArgs.OldItems)
                        {
                            calendar.SelectedDates.Remove(item);
                        }
                        foreach (DateTime item in cArgs.NewItems)
                        {
                            calendar.SelectedDates.Add(item);
                        }
                        break;
                    case NotifyCollectionChangedAction.Move:
                        // ignored
                        break;
                    case NotifyCollectionChangedAction.Reset:
                        // ignored
                        break;
                    default:
                        break;
                }
            }

            // 这里
            sourceCollection.CollectionChanged -= SelectedDatesOnCollectionChanged;
            sourceCollection.CollectionChanged += SelectedDatesOnCollectionChanged;
        }

        #endregion

    }

使用起来就简单了:

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
WPF ,可以使用 Binding 绑定 Click 事件,但需要使用一个特殊的附加属性来实现。具体步骤如下: 1. 在 XAML 定义 Button 控件,并使用 Binding 绑定 Click 事件,例如: ``` <Button Content="Click Me" local:ButtonClick.Command="{Binding MyCommand}" /> ``` 其,ButtonClick 是一个自定义附加属性,可以在代码定义,MyCommand 是一个 ICommand 类型的属性,表示要执行的命令。 2. 在代码定义 ButtonClick 附加属性,并在其 PropertyChangedCallback 注册 Click 事件的处理方法,例如: ``` public static class ButtonClick { public static readonly DependencyProperty CommandProperty = DependencyProperty.RegisterAttached("Command", typeof(ICommand), typeof(ButtonClick), new FrameworkPropertyMetadata(null, CommandChanged)); public static ICommand GetCommand(Button button) { return (ICommand)button.GetValue(CommandProperty); } public static void SetCommand(Button button, ICommand value) { button.SetValue(CommandProperty, value); } private static void CommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { if (d is Button button) { button.Click -= Button_Click; if (e.NewValue is ICommand command) { button.Click += Button_Click; } } } private static void Button_Click(object sender, RoutedEventArgs e) { if (sender is Button button) { ICommand command = GetCommand(button); if (command != null && command.CanExecute(button.CommandParameter)) { command.Execute(button.CommandParameter); } } } } ``` 其,CommandProperty 是 ButtonClick 附加属性的依赖属性,GetCommand 和 SetCommand 方法用于获取和设置 Command 属性的值,CommandChanged 方法在 Command 属性值发生变化时注册或取消注册 Click 事件的处理方法,Button_Click 方法是 Click 事件的处理方法,用于执行绑定的命令。 注意,使用这种方式绑定 Click 事件时,Button 控件的 Click 事件不会触发,而是会触发 ButtonClick 附加属性的处理方法。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值