1

I am new in wpf and I am having quite a hard time trying to figure out how to notify a multiconverter that its binded collections have changed. I have tried several solutions but nothing works.

I have a calendar with a multiconverter allowing me to change calendardaybutton background according to 2 Observablecollection. (That woks, If I put data manually in the collections before datacontext is applied, I can see days are colored).

When user click on the calendar, the selected date is added to one of the collections.

I have checked, the new date is correctly added to the collection, but I don't see any change on the calendar (the clicked calendardaybutton background should change).

Here is my XAML:

 <Window.Resources>
    <local:DateConverter x:Key="dateConverter" />
    <local:MyViewModel x:Key="myViewModel" />
</Window.Resources>

<Grid>
    <Calendar x:Name="MyCal" 
              SelectionMode="MultipleRange" 
              SelectedDatesChanged="OnSelectedDatesChanged">
        <Calendar.CalendarDayButtonStyle>
            <Style TargetType="CalendarDayButton">
                <Setter Property="Background" >
                    <Setter.Value>
                        <MultiBinding Converter="{StaticResource dateConverter}">
                            <Binding/>
                            <Binding Source="{StaticResource myViewModel}" Path="MyHolidayCollection"  UpdateSourceTrigger="PropertyChanged"/>
                            <Binding Source="{StaticResource myViewModel}" Path="ShutDownDateCollection" UpdateSourceTrigger="PropertyChanged"/>
                        </MultiBinding>
                    </Setter.Value>
                </Setter>
            </Style>
        </Calendar.CalendarDayButtonStyle>
    </Calendar>
 </Grid>`

My DataContext is a ViewModel containing the 2 observable collections

    public partial class MainWindow : Window
{
    MyViewModel myViewModel;
    public MainWindow()
    {
        myViewModel = new MyViewModel();
        DataContext = myViewModel;

        InitializeComponent();

    }

    private void OnSelectedDatesChanged(object sender, RoutedEventArgs e)
    {
        Calendar c = (Calendar)sender;
        myViewModel.AssignDate(c.SelectedDate);
    }
}

Here is my ViewModel (inherited from ViewModelBase, implementing INotifyPropertyChanged)

public class MyViewModel:ViewModelBase
{
    private ObservableCollection<DateTime?> _holidayDateCollection;
    public ObservableCollection<DateTime?> HolidayDateCollection
    {
        get { return _holidayDateCollection; }
        set
        {
            _holidayDateCollection = value;
            OnPropertyChanged("HolidayDateCollection");
          }
    }

    private ObservableCollection<DateTime?> _shutDownDateCollection;
    public ObservableCollection<DateTime?> ShutDownDateCollection
    {
        get { return _holidayDateCollection; }
        set
        {
            _holidayDateCollection = value;
            OnPropertyChanged("ShutDownDateCollection");
        }
    }

    public MyViewModel()
    {
        HolidayDateCollection = new ObservableCollection<DateTime?>();
        ShutDownDateCollection = new ObservableCollection<DateTime?>();
    }


    public void AssignDate(DateTime? date)
    {
       HolidayDateCollection.Add(date);
    }

And here is my multiconverter

public class DateConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        SolidColorBrush s = new SolidColorBrush(Colors.LightBlue);
        SolidColorBrush s1 = new SolidColorBrush(Colors.Yellow);
        SolidColorBrush s2 = new SolidColorBrush(Colors.Red);

        if (((ObservableCollection<DateTime?>)values[1]).Contains((DateTime?)values[0])) { return s1; }
        if (((ObservableCollection<DateTime?>)values[2]).Contains((DateTime?)values[0])) { return s2; }

        return s;//normal day
    }



    public object[] ConvertBack(object value, Type[] targetTypes,
           object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotSupportedException("Cannot convert back");
    }
}

I tried a lot of solutions (that I removed to clear the code here) but nothing seems to works.

Any help or advise would be much appreciated, i am stuck for days now. Thank you.

Felix D.
  • 3,957
  • 4
  • 35
  • 61
Jlo
  • 51
  • 7
  • Put a breakpoint on the NSE you throw in the `ConvertBack`, and see if you are hitting it when setting your value as you describe. If you are hitting it then that's your problem. – slugster Sep 03 '18 at 07:11
  • No It doesn't hit it. – Jlo Sep 03 '18 at 07:36

1 Answers1

3

You are referencing two completely different ViewModel instances which is why you are facing the issue.

Instance 1: (inside xaml)

<Window.Resources>
    <local:DateConverter x:Key="dateConverter" />
    <local:MyViewModel x:Key="myViewModel" />
</Window.Resources>

Instance 2: (in your code behind file)

myViewModel = new MyViewModel();
DataContext = myViewModel;

To fix the issue, change MyViewModel instance from either one. For example, after removing form xaml your code will look like:

<Window.Resources>
    <local:DateConverter x:Key="dateConverter" />
</Window.Resources>

<Grid>
    <Calendar x:Name="MyCal" 
              SelectionMode="MultipleRange" 
              SelectedDatesChanged="OnSelectedDatesChanged">
        <Calendar.CalendarDayButtonStyle>
            <Style TargetType="CalendarDayButton">
                <Setter Property="Background" >
                    <Setter.Value>
                        <MultiBinding Converter="{StaticResource dateConverter}">
                            <Binding />
                            <Binding RelativeSource="{RelativeSource AncestorType=Window, Mode=FindAncestor}" Path="DataContext.HolidayDateCollection"  UpdateSourceTrigger="PropertyChanged"/>
                            <Binding RelativeSource="{RelativeSource AncestorType=Window, Mode=FindAncestor}" Path="DataContext.ShutDownDateCollection" UpdateSourceTrigger="PropertyChanged"/>
                        </MultiBinding>
                    </Setter.Value>
                </Setter>
            </Style>
        </Calendar.CalendarDayButtonStyle>
    </Calendar>
 </Grid>
Dipen Shah
  • 23,609
  • 1
  • 21
  • 48
  • I follow your advice (removing the local myViewmodel and binding with Path="HolidayCollection" ) But It seems that it cannot find the collection ( the values[1] in my converter is a DependencyProperty.UnsetValue) – Jlo Sep 03 '18 at 08:05
  • @Jlo Works fine for me, check if your are using `HolidayDateCollection` property name in xaml and view model. – Dipen Shah Sep 03 '18 at 08:09
  • Exception I get on the XAML designer: InvalidCastException: Unable to cast object of type 'MS.Internal.NamedObject' to type 'System.Collections.ObjectModel.ObservableCollection`1[System.Nullable`1[System.DateTime]]'. – Jlo Sep 03 '18 at 08:15
  • Check my edits. Also on first render cycle you datacontext will be null and you 'll always get UnsetValue so it in your converter always check for that. – Dipen Shah Sep 03 '18 at 08:28
  • Yes I have added a protection !=null. Thanks the edit is working fine. Thank you very much :-) what is the good way to Notify the PropertyChanged just after I clicked on the day (I need to refresh the calendar to see the changes for now). The calendar will change if I renew the collection HolidayDateCollection, but not if use Add(..) – Jlo Sep 03 '18 at 08:37
  • Thanks again for efficient and quick help ;-) (just need to work a bit on INotifyCollectionChanged for the added day which show up on calendar only after I refresh). – Jlo Sep 03 '18 at 09:31