问题背景

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

问题来了。

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

解决思路:

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

主要代码如下:

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
}

使用起来就简单了:

<Calender Grid.Column="0" x:Name="Calendar" Width=260" Heigh="270"
SelectionMode="MultipleRange" lui:CalendarEx.SelectedDatesSource="{Binding Checkins}"
lui:BehaviorRepo.ReleaseStylusCaptureCalendar="True"
/>

WPF 只读集合在 XAML 中的绑定(WPF:Binding for readonly collection in xaml)的更多相关文章

  1. WPF 的 ElementName 在 ContextMenu 中无法绑定成功?试试使用 x:Reference!

    在 Binding 中使用 ElementName 司空见惯,没见它出过什么事儿.不过当你预见 ContextMenu,或者类似 Grid.Row / Grid.Column 这样的属性中设置的时候, ...

  2. WPF绑定xaml中绑定对象需用属性表示,字段不可以绑定

    在练习WPF绑定时发现对象属性可以在XAML中绑定,但字段是不可以绑定: 比如: private Person person{get;set;}  可以绑定到XAML中,<TextBox Nam ...

  3. 整理:WPF中Xaml中绑定枚举的写法

    原文:整理:WPF中Xaml中绑定枚举的写法 目的:在Combobox.ListBox中直接绑定枚举对象的方式,比如:直接绑定字体类型.所有颜色等枚举类型非常方便 一.首先用ObjectDataPro ...

  4. ComboBox在WPF中的绑定示例:绑定项、集合、转换,及其源代码

    示例1.Selector(基类) 的示例Controls/SelectionControl/SelectorDemo.xaml <Page x:Class="Windows10.Con ...

  5. WPF Xaml中创建集合

    首先在xaml中创建集合是一个不可取的方法. 本方法仅作为xaml的学习. 本文略微无聊,主要是编写的东西都是老玩意. 首先是定义一个类,作为你要加载集合的模型. 结构如下 internal clas ...

  6. WPF 中双向绑定通知机制之ObservableCollection使用

    msdn中   ObservableCollection<T> 类    表示一个动态数据集合,在添加项.移除项或刷新整个列表时,此集合将提供通知. 在许多情况下,所使用的数据是对象的集合 ...

  7. WPF ContextMenu 在MVVM模式中绑定 Command及使用CommandParameter传参

    原文:WPF ContextMenu 在MVVM模式中绑定 Command及使用CommandParameter传参 ContextMenu无论定义在.cs或.xaml文件中,都不继承父级的DataC ...

  8. 封装:WPF中可以绑定的BindPassWord控件

    原文:封装:WPF中可以绑定的BindPassWord控件 一.目的:本身自带的PassWord不支持绑定 二.Xaml部分 <UserControl x:Class="HeBianG ...

  9. 使用异步方法在XAML中绑定系统时间

    最近的工作需要在程序界面上显示实时的系统时间,网上查了查大部分都是用Timer或者线程来实现. 个人非常不喜欢用Timer,感觉这东西有点太耗资源,然后思考了下觉得更好的方法应该是使用异步的方法在委托 ...

随机推荐

  1. Session、Cookie与Token

    http协议是无状态协议 协议是指计算机通信网络中两台计算机之间进行通信所必须共同遵守的规定或规则,超文本传输协议(HTTP)是一种通信协议,它允许将超文本标记语言(HTML)文档从Web服务器传送到 ...

  2. 【Linux】nohup和&的区别

    同样都是后台执行进程,但是nohup和&有什么区别呢? & 是指后台运行: nohup 的功能和& 之间的功能并不相同. 其中,nohup 可以使得命令永远运行下去和用户终端没 ...

  3. [mysql]ERROR 1364 (HY000): Field 'ssl_cipher' doesn't have a default value

    转载自:http://www.cnblogs.com/joeblackzqq/p/4526589.html From: http://m.blog.csdn.net/blog/langkeziju/1 ...

  4. CVE-2018-1273 Spring Data Commons 远程命令执行漏洞复现

    一.漏洞描述 Spring Data是一个用于简化数据库访问,并支持云服务的开源框架,Spring Data Commons是Spring Data下所有子项目共享的基础框架.Spring Data ...

  5. VMware下安装Ubantu 18.04

    一.VIM安装及配置 1.安装VIM sudo apt-get install vim 二.拼音输入法以及搜狗拼音输入法安装 1.安装Fcitx输入框架 sudo apt-get install fc ...

  6. Docker相关简介以及使用方法

    Docker: 可以把它看作是一个软件,在这个软件当中呢,还可以安装其他的软件,还可以把软件所需要的环境依赖一起添加进来,这样让开发人员的程序在不同的环境当中都可以流转起来,避免了程序出现" ...

  7. 中断与系统调用深度分析(以网络编程接口SocketAPI为例)

    1.从计算机CPU与I/O设备的交互方式谈起 计算机CPU与I/O设备的交互方式有最早的程序查询(也叫轮询)方式,发展到后来的程序中断方式,DMA方式等.简单来说,最早的程序查询方式的机制是,CPU若 ...

  8. django之orm单表查询

    这几天重新学习了一下django的orm,以此作为记录来分享. Part1:修改配置,生成表 在写数据和查数据之前,首先先得把django配置一下,具体配置如下: 1.先在公共项目的settings中 ...

  9. 从软件(Java/hotspot/Linux)到硬件(硬件架构)分析互斥操作的本质

    先上结论: 一切互斥操作的依赖是 自旋锁(spin_lock),互斥量(semaphore)等其他需要队列的实现均需要自选锁保证临界区互斥访问. 而自旋锁需要xcmpchg等类似的可提供CAS操作的硬 ...

  10. 并发条件队列之Condition 精讲

    1. 条件队列的意义 Condition将Object监控器方法( wait , notify和notifyAll )分解为不同的对象,从而通过与任意Lock实现结合使用,从而使每个对象具有多个等待集 ...