Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to Bind BlackoutDates in WPF Toolkit Calendar control?

I'd like to bind a list of dates to the BlackoutDates property but it doesn't really seem to possible. Especially in a MVVM scenario. Has anyone accomplished something like this? Are there any good calendar controls that play nice with MVVM?

like image 624
nportelli Avatar asked Oct 28 '09 15:10

nportelli


3 Answers

For your DatePicker dilemma, I found a neat hack using attached properties (modified from my use of 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
}

I'm sure I'm too late to help you out, but hopefully someone else will find it useful.

like image 83
Matt DeKrey Avatar answered Nov 01 '22 13:11

Matt DeKrey


Here is an improved version of Matt's answer that allows us to work with the BlackoutDates as with any normal Observable collection (you don't need to create new collections each time you want to change the BlackoutDates). We store a list of all the calendars and datepickers binded and inside their tag we store the collection used in MVVM. An easy modification of the class will allow to work with ObservableCollection<DateTime> if needed:

// 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
}

Here is the ObservableCollection<DateTime> version:

// 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
}
like image 38
Ignacio Soler Garcia Avatar answered Nov 01 '22 12:11

Ignacio Soler Garcia


I implemented the above example (the AttachedProperties class). I created a property in my Viewmodel like this:

    public CalendarBlackoutDatesCollection BlackoutDates
    {
        get
        {
            return _blackoutDates;
        }
        set
        {
            _blackoutDates = value;
            this.RaisePropertyChanged(p => p.BlackoutDates);
        }
    }

This ViewModel inerits from 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));
                }
            }
         }
     }

This is the Xaml in the window that uses this property:

  <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>

Now when I want to add BlackoutDates to the calendar, I call UpdateCalendarBlackoutDates in my 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);
    }

This works perfectly for me. It could be further perfected by changing the OnRegisterCommandBindingChanged method to accept a List of DateRanges instead of a CalendarBlackoutDatesCollection, and changing the property to a List like this:

public List<CalendarDateRange> BlackoutDates
{
  etc.

but for now this works for me..

like image 23
wouter4 Avatar answered Nov 01 '22 13:11

wouter4