如何在WPF工具包日历控件中绑定BlackoutDates?

我想将一个日期列表绑定到BlackoutDates属性,但它似乎不太可能。 特别是在MVVM场景中。 有人做过这样的事吗? 有没有好的日历控件可以与MVVM配合使用?

对于你的DatePicker困境,我发现了一个使用附加属性的简洁黑客(从我使用CommandBindings修改):

class AttachedProperties : DependencyObject { #region RegisterBlackoutDates // Adds a collection of command bindings to a date picker's existing BlackoutDates collection, since the collections are immutable and can't be bound to otherwise. // // Usage:  public static DependencyProperty RegisterBlackoutDatesProperty = DependencyProperty.RegisterAttached("RegisterBlackoutDates", typeof(System.Windows.Controls.CalendarBlackoutDatesCollection), typeof(AttachedProperties), new PropertyMetadata(null, OnRegisterCommandBindingChanged)); public static void SetRegisterBlackoutDates(UIElement element, System.Windows.Controls.CalendarBlackoutDatesCollection value) { if (element != null) element.SetValue(RegisterBlackoutDatesProperty, value); } public static System.Windows.Controls.CalendarBlackoutDatesCollection GetRegisterBlackoutDates(UIElement element) { return (element != null ? (System.Windows.Controls.CalendarBlackoutDatesCollection)element.GetValue(RegisterBlackoutDatesProperty) : null); } private static void OnRegisterCommandBindingChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) { System.Windows.Controls.DatePicker element = sender as System.Windows.Controls.DatePicker; if (element != null) { System.Windows.Controls.CalendarBlackoutDatesCollection bindings = e.NewValue as System.Windows.Controls.CalendarBlackoutDatesCollection; if (bindings != null) { element.BlackoutDates.Clear(); foreach (var dateRange in bindings) { element.BlackoutDates.Add(dateRange); } } } } #endregion } 

我确定我来不及帮助你,但希望其他人会觉得它很有用。

这是Matt答案的改进版本,它允许我们像使用任何普通的Observable集合一样使用BlackoutDates(每次要更改BlackoutDates时都不需要创建新的集合)。 我们存储了所有日历和日期删除器的列表,并在其标签内部存储了MVVM中使用的集合。 如果需要,对类的简单修改将允许使用ObservableCollection

 // Adds a collection of command bindings to a date picker's existing BlackoutDates collection, since the collections are immutable and can't be bound to otherwise. // Usage:  public class CalendarAttachedProperties : DependencyObject { #region Attributes private static readonly List _calendars = new List(); private static readonly List _datePickers = new List(); #endregion #region Dependency Properties public static DependencyProperty RegisterBlackoutDatesProperty = DependencyProperty.RegisterAttached("RegisterBlackoutDates", typeof(CalendarBlackoutDatesCollection), typeof(CalendarAttachedProperties), new PropertyMetadata(null, OnRegisterCommandBindingChanged)); public static void SetRegisterBlackoutDates(DependencyObject d, CalendarBlackoutDatesCollection value) { d.SetValue(RegisterBlackoutDatesProperty, value); } public static CalendarBlackoutDatesCollection GetRegisterBlackoutDates(DependencyObject d) { return (CalendarBlackoutDatesCollection)d.GetValue(RegisterBlackoutDatesProperty); } #endregion #region Event Handlers private static void CalendarBindings_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) { CalendarBlackoutDatesCollection blackoutDates = sender as CalendarBlackoutDatesCollection; Calendar calendar = _calendars.First(c => c.Tag == blackoutDates); if (e.Action == NotifyCollectionChangedAction.Add) { foreach (CalendarDateRange dateRange in e.NewItems) { calendar.BlackoutDates.Add(dateRange); } } } private static void DatePickerBindings_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) { CalendarBlackoutDatesCollection blackoutDates = sender as CalendarBlackoutDatesCollection; DatePicker datePicker = _datePickers.First(c => c.Tag == blackoutDates); if (e.Action == NotifyCollectionChangedAction.Add) { foreach (CalendarDateRange dateRange in e.NewItems) { datePicker.BlackoutDates.Add(dateRange); } } } #endregion #region Private Methods private static void OnRegisterCommandBindingChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) { Calendar calendar = sender as Calendar; if (calendar != null) { CalendarBlackoutDatesCollection bindings = e.NewValue as CalendarBlackoutDatesCollection; if (bindings != null) { if (!_calendars.Contains(calendar)) { calendar.Tag = bindings; _calendars.Add(calendar); } calendar.BlackoutDates.Clear(); foreach (var dateRange in bindings) { calendar.BlackoutDates.Add(dateRange); } bindings.CollectionChanged += CalendarBindings_CollectionChanged; } } else { DatePicker datePicker = sender as DatePicker; if (datePicker != null) { CalendarBlackoutDatesCollection bindings = e.NewValue as CalendarBlackoutDatesCollection; if (bindings != null) { if (!_datePickers.Contains(datePicker)) { datePicker.Tag = bindings; _datePickers.Add(datePicker); } datePicker.BlackoutDates.Clear(); foreach (var dateRange in bindings) { datePicker.BlackoutDates.Add(dateRange); } bindings.CollectionChanged += DatePickerBindings_CollectionChanged; } } } } #endregion } 

这是ObservableCollection 版本:

 // Adds a collection of command bindings to a date picker's existing BlackoutDates collection, since the collections are immutable and can't be bound to otherwise. // Usage:  public class CalendarAttachedProperties : DependencyObject { #region Attributes private static readonly List _calendars = new List(); private static readonly List _datePickers = new List(); #endregion #region Dependency Properties public static DependencyProperty RegisterBlackoutDatesProperty = DependencyProperty.RegisterAttached("RegisterBlackoutDates", typeof(ObservableCollection), typeof(CalendarAttachedProperties), new PropertyMetadata(null, OnRegisterCommandBindingChanged)); public static void SetRegisterBlackoutDates(DependencyObject d, ObservableCollection value) { d.SetValue(RegisterBlackoutDatesProperty, value); } public static ObservableCollection GetRegisterBlackoutDates(DependencyObject d) { return (ObservableCollection)d.GetValue(RegisterBlackoutDatesProperty); } #endregion #region Event Handlers private static void CalendarBindings_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) { ObservableCollection blackoutDates = sender as ObservableCollection; Calendar calendar = _calendars.First(c => c.Tag == blackoutDates); if (e.Action == NotifyCollectionChangedAction.Add) { foreach (DateTime date in e.NewItems) { calendar.BlackoutDates.Add(new CalendarDateRange(date)); } } } private static void DatePickerBindings_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) { ObservableCollection blackoutDates = sender as ObservableCollection; DatePicker datePicker = _datePickers.First(c => c.Tag == blackoutDates); if (e.Action == NotifyCollectionChangedAction.Add) { foreach (DateTime date in e.NewItems) { datePicker.BlackoutDates.Add(new CalendarDateRange(date)); } } } #endregion #region Private Methods private static void OnRegisterCommandBindingChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) { Calendar calendar = sender as Calendar; if (calendar != null) { ObservableCollection bindings = e.NewValue as ObservableCollection; if (bindings != null) { if (!_calendars.Contains(calendar)) { calendar.Tag = bindings; _calendars.Add(calendar); } calendar.BlackoutDates.Clear(); foreach (DateTime date in bindings) { calendar.BlackoutDates.Add(new CalendarDateRange(date)); } bindings.CollectionChanged += CalendarBindings_CollectionChanged; } } else { DatePicker datePicker = sender as DatePicker; if (datePicker != null) { ObservableCollection bindings = e.NewValue as ObservableCollection; if (bindings != null) { if (!_datePickers.Contains(datePicker)) { datePicker.Tag = bindings; _datePickers.Add(datePicker); } datePicker.BlackoutDates.Clear(); foreach (DateTime date in bindings) { datePicker.BlackoutDates.Add(new CalendarDateRange(date)); } bindings.CollectionChanged += DatePickerBindings_CollectionChanged; } } } } #endregion } 

我实现了上面的例子(AttachedProperties类)。 我在我的Viewmodel中创建了一个属性,如下所示:

  public CalendarBlackoutDatesCollection BlackoutDates { get { return _blackoutDates; } set { _blackoutDates = value; this.RaisePropertyChanged(p => p.BlackoutDates); } } 

来自ObservableBase的ViewModel工具:

  using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ComponentModel; using System.Windows.Data; using System.Collections; namespace MySolution { public abstract class ObservableBase : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; public void RaisePropertyChanged(string propertyName) { if (this.PropertyChanged != null) { this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } } } 

这是窗口中使用此属性的Xaml:

       

现在,当我想将BlackoutDates添加到日历中时,我在ViewModel中调用UpdateCalendarBlackoutDates:

  private void UpdateCalendarBlackoutDates() { CalendarDateRange r = new CalendarDateRange(new DateTime(2010, 12, 9), new DateTime(2010, 12, 9)); CalendarDateRange r2 = new CalendarDateRange(new DateTime(2010, 12, 10), new DateTime(2010, 12, 10)); // Because we can't reach the real calendar from the viewmodel, and we can't create a // new CalendarBlackoutDatesCollection without specifying a Calendar to // the constructor, we provide a "Dummy calendar", only to satisfy // the CalendarBlackoutDatesCollection... // because you can't do: BlackoutDates = new CalendarBlackoutDatesCollection(). Calendar dummyCal = new Calendar(); BlackoutDates = new CalendarBlackoutDatesCollection(dummyCal); // Add the dateranges to the BlackOutDates property BlackoutDates.Add(r); BlackoutDates.Add(r2); } 

这对我来说很完美。 通过更改OnRegisterCommandBindingChanged方法以接受DateRanges列表而不是CalendarBlackoutDatesCollection,并将属性更改为List,可以进一步完善它:

 public List BlackoutDates { etc. 

但是现在这对我有用..