我想将一个日期列表绑定到BlackoutDates属性,但它似乎不太可能。特别是在MVVM场景中。有没有人做过这样的事情?是否有任何好的日历控件可以与MVVM配合使用?
答案 0 :(得分:16)
对于你的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: <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
}
我确定我来不及帮助你,但希望其他人会觉得它很有用。
答案 1 :(得分:8)
这是Matt答案的改进版本,它允许我们像使用任何普通的Observable集合一样使用BlackoutDates(每次想要更改BlackoutDates时都不需要创建新的集合)。我们存储了所有日历和日期删除器的列表,并在其标签内部存储了MVVM中使用的集合。对类的简单修改将允许使用ObservableCollection&lt; DateTime&gt;如果需要:
// 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
}
这是ObservableCollection&lt; DateTime&gt;版本:
// 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
}
答案 2 :(得分:6)
我实现了上面的例子(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添加到日历中时,我在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<CalendarDateRange> BlackoutDates
{
etc.
但是现在这对我有用..