Como BlackoutDates Bind no controle Calendar WPF Toolkit?
-
08-07-2019 - |
Pergunta
Eu gostaria de vincular uma lista de datas para a propriedade BlackoutDates mas ele realmente não parece possível. Especialmente em um cenário MVVM. Alguém já realizou algo assim? Existem quaisquer controles bom calendário que jogar bonito com MVVM?
Solução
Para o seu dilema DatePicker, eu encontrei um hack puro usando propriedades anexadas (modificado a partir do meu uso de 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: <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
}
Eu tenho certeza que estou muito tarde para ajudá-lo, mas espero que alguém vai encontrá-lo útil.
Outras dicas
Aqui é uma versão melhorada da resposta de Matt que nos permite trabalhar com os BlackoutDates como com qualquer coleção observável normal (não é necessário para criar novas coleções cada vez que você quiser alterar as BlackoutDates). Nós armazenar uma lista de todos os calendários e datepickers binded e dentro de sua tag que armazenar a coleção usada em MVVM. Uma modificação fácil da classe permitirá trabalhar com 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: <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
}
Aqui está o 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: <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
}
Eu implementei o exemplo acima (a classe AttachedProperties). Eu criei uma propriedade em meu ViewModel como esta:
public CalendarBlackoutDatesCollection BlackoutDates
{
get
{
return _blackoutDates;
}
set
{
_blackoutDates = value;
this.RaisePropertyChanged(p => p.BlackoutDates);
}
}
Este inerits ViewModel de 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));
}
}
}
}
Este é o XAML na janela que usa essa propriedade:
<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>
Agora, quando eu quero adicionar BlackoutDates ao calendário, eu chamo UpdateCalendarBlackoutDates na minha 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);
}
Isso funciona perfeitamente para mim. Pode ser ainda mais aperfeiçoado, alterando o método OnRegisterCommandBindingChanged para aceitar uma lista de DateRanges em vez de um CalendarBlackoutDatesCollection e alterar a propriedade de uma lista como esta:
public List<CalendarDateRange> BlackoutDates
{
etc.
mas por enquanto isso funciona para mim ..