Я хотел бы привязать список дат к свойству BlackoutDates, но это действительно не представляется возможным. Особенно в сценарии MVVM. Кто-нибудь сделал что-то подобное? Есть ли хорошие календарные элементы управления, которые хорошо сочетаются с MVVM?
Как связать BlackoutDates в WPF Toolkit Calendar control?
Ответ 1
Для вашей дилеммы DatePicker я нашел аккуратный взломать с помощью подключенных свойств (измененный из моего использования CommandBindings):
class AttachedProperties : DependencyObject
{
#region RegisterBlackoutDates
// Adds a collection of command bindings to a date picker existing BlackoutDates collection, since the collections are immutable and can't be bound to otherwise.
//
// Usage: <DatePicker hacks:AttachedProperties.RegisterBlackoutDates="{Binding BlackoutDates}" >
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
}
Я уверен, что я слишком поздно, чтобы помочь вам, но, надеюсь, кто-то еще найдет это полезным.
Ответ 2
Вот улучшенная версия ответа Matt, которая позволяет нам работать с BlackoutDates как с любой обычной коллекцией Observable (вам не нужно создавать новые коллекции каждый раз, когда вы хотите изменить BlackoutDates). Мы сохраняем список всех календарей и привязанных датпиксеров, и внутри их тега мы храним коллекцию, используемую в MVVM. Легкая модификация класса позволит работать с ObservableCollection <DateTime> при необходимости:
// Adds a collection of command bindings to a date picker existing BlackoutDates collection, since the collections are immutable and can't be bound to otherwise.
// Usage: <DatePicker CalendarAttachedProperties.RegisterBlackoutDates="{Binding BlackoutDates}" >
public class CalendarAttachedProperties : DependencyObject
{
#region Attributes
private static readonly List<Calendar> _calendars = new List<Calendar>();
private static readonly List<DatePicker> _datePickers = new List<DatePicker>();
#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 <DateTime> версия:
// Adds a collection of command bindings to a date picker existing BlackoutDates collection, since the collections are immutable and can't be bound to otherwise.
// Usage: <DatePicker hacks:AttachedProperties.RegisterBlackoutDates="{Binding BlackoutDates}" >
public class CalendarAttachedProperties : DependencyObject
{
#region Attributes
private static readonly List<Calendar> _calendars = new List<Calendar>();
private static readonly List<DatePicker> _datePickers = new List<DatePicker>();
#endregion
#region Dependency Properties
public static DependencyProperty RegisterBlackoutDatesProperty = DependencyProperty.RegisterAttached("RegisterBlackoutDates", typeof(ObservableCollection<DateTime>), typeof(CalendarAttachedProperties), new PropertyMetadata(null, OnRegisterCommandBindingChanged));
public static void SetRegisterBlackoutDates(DependencyObject d, ObservableCollection<DateTime> value)
{
d.SetValue(RegisterBlackoutDatesProperty, value);
}
public static ObservableCollection<DateTime> GetRegisterBlackoutDates(DependencyObject d)
{
return (ObservableCollection<DateTime>)d.GetValue(RegisterBlackoutDatesProperty);
}
#endregion
#region Event Handlers
private static void CalendarBindings_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
ObservableCollection<DateTime> blackoutDates = sender as ObservableCollection<DateTime>;
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<DateTime> blackoutDates = sender as ObservableCollection<DateTime>;
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<DateTime> bindings = e.NewValue as ObservableCollection<DateTime>;
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<DateTime> bindings = e.NewValue as ObservableCollection<DateTime>;
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
}
Ответ 3
Я реализовал приведенный выше пример (класс AttachedProperties). Я создал свойство в моей Viewmodel следующим образом:
public CalendarBlackoutDatesCollection BlackoutDates
{
get
{
return _blackoutDates;
}
set
{
_blackoutDates = value;
this.RaisePropertyChanged(p => p.BlackoutDates);
}
}
Этот объект ViewModel вводит из ObservableBase:
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 в окне, которое использует это свойство:
<Window x:Class="MySolution.MainWindow"
xmlns:local="clr-namespace:MySolution">
<Grid>
<DatePicker x:Name="datePicker" Grid.Row="0" Height="30"
local:AttachedProperties.RegisterBlackoutDates="{Binding BlackoutDates}">
</DatePicker>
</Grid>
Теперь, когда я хочу добавить BlackoutDates в календарь, я вызываю UpdateCalendarBlackoutDates в моей ViewModel:
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, чтобы принять List of DateRanges вместо CalendarBlackoutDatesCollection и изменить свойство на List следующим образом:
public List<CalendarDateRange> BlackoutDates
{
etc.
но пока это работает для меня..