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

时间:2009-10-28 15:46:33

标签: c# wpf mvvm data-binding calendar

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

3 个答案:

答案 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.

但是现在这对我有用..