Reputation: 59
I am trying to highlight dates with the color "LigthPink" for important dates of the appointments that are scheduled. In my project in WPF MVVM, I created a code, but I cannot update the dates.
I arrived at the following code:
class ConverterHigligthdate: IValueConverter
{
static BindableCollection<DateTime> dict = new BindableCollection<DateTime>();
public event PropertyChangedEventHandler PropertyChanged;
static ConverterHigligthdate()
{
dict.Add(DateTime.Today);
dict.Add(DateTime.Today.AddDays(2));
dict.Add(DateTime.Today.AddDays(-10));
dict.Add(DateTime.Today.AddDays(-20));
dict.Add(DateTime.Today.AddDays(-15));
}
public static void AddDate(DateTime date)
{
dict.Add(date);
}
public static void RemoveDate(DateTime date)
{
dict.Remove(date);
}
public void Clear()
{
dict.Clear();
dict.Refresh();
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
string text = null;
if (dict.Contains((DateTime)value))
text = null;
else
text = "";
return text;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
And in the view:
<Window.Resources>
<local:ConverterHigligthdate x:Key="ConverterHigligthdate"/>
<Style x:Key="calendarDayButtonStyle" TargetType="{x:Type CalendarDayButton}">
<Setter Property="Margin" Value="8"/>
<Setter Property="FontSize" Value="13"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Converter={StaticResource ConverterHigligthdate}}" Value="{x:Null}">
<Setter Property="Background" Value="LightPink"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid Margin="5">
<Calendar SelectionMode="MultipleRange" CalendarDayButtonStyle="{DynamicResource calendarDayButtonStyle}"/>
</Grid>
Does anyone know how to implement something that makes this work?
Upvotes: 1
Views: 2024
Reputation: 16148
You're going about this the wrong way. With MVVM you always do business logic in your view model layer, never in your converters (they're part of the view layer).
There are numerous ways of going about this, but generally you want your view model layer to prepare your data in a format that the view can readily consume. For the purpose of performance, let's wrap all your selected dates in a lookup table:
public class MainViewModel
{
public HashSet<DateTime> Dates { get; } = new HashSet<DateTime>();
public MainViewModel()
{
// highlight today and tomorrow
this.Dates.Add(DateTime.Today);
this.Dates.Add(DateTime.Today.AddDays(1));
}
}
Now in your CalendarDayButtonStyle you want to add a DataTrigger. When the date for the button in question is in your collection, that's when you want to change the background color:
<Style x:Key="CalendarDayButtonStyle" TargetType="CalendarDayButton">
<Style.Triggers>
<DataTrigger Value="True">
<DataTrigger.Binding>
<MultiBinding Converter="{StaticResource LookupConverter}">
<Binding />
<Binding Path="DataContext.Dates" RelativeSource="{RelativeSource AncestorType=Calendar}" />
</MultiBinding>
</DataTrigger.Binding>
<Setter Property="Background" Value="Pink" />
</DataTrigger>
</Style.Triggers>
</Style>
So all you need now is a converter to do the lookup. We need to pass in both the lookup table as well as the value to look up, so we can use a MultiBinding. This is in fact logic that could have been placed in the view model if we really wanted to, but it doesn't reference any view-model specific data, and it can be re-used elsewhere, so we'll bend the rules a tiny bit:
public class LookupConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
var date = (DateTime)values[0];
var dates = values[1] as HashSet<DateTime>;
return dates.Contains(date);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
And that's all you need. Result:
Upvotes: 3