Как связать BlackoutDates в элементе управления календаря WPF Toolkit? - PullRequest
10 голосов
/ 28 октября 2009

Я бы хотел связать список дат со свойством BlackoutDates, но на самом деле это не представляется возможным. Особенно в сценарии MVVM. Кто-нибудь сделал что-то подобное? Есть ли хорошие элементы управления календарем, которые хорошо работают с MVVM?

Ответы [ 3 ]

16 голосов
/ 07 июля 2010

Для вашей дилеммы 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
}

Я уверен, что слишком поздно, чтобы помочь вам, но, надеюсь, кто-то найдет это полезным.

8 голосов
/ 13 марта 2012

Вот улучшенная версия ответа Мэтта, которая позволяет нам работать с BlackoutDates как с любой обычной коллекцией Observable (вам не нужно создавать новые коллекции каждый раз, когда вы хотите изменить 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: <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 версия:

// 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
}
6 голосов
/ 11 декабря 2010

Я реализовал приведенный выше пример (класс 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 для принятия списка DateRanges вместо CalendarBlackoutDatesCollection и изменив свойство на List следующим образом:

public List<CalendarDateRange> BlackoutDates
{
  etc.

но сейчас это работает для меня ..

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...