UWP开发:实现CalendarView选中日期时自动高亮整周
更优雅的UWP CalendarView整周高亮方案
我之前做UWP项目时也碰到过这个头疼的问题——手动绑定SelectedDatesChanged事件来维护整周选中状态,不仅代码冗余,还特别容易在月份切换、重复点击时出现状态错乱。这里有两个更简洁的方案,帮你摆脱繁琐的事件处理:
方案一:用附加属性封装整周选择逻辑
这种方式把整周选择的逻辑完全封装成一个可复用的附加属性,页面后台不用写任何事件代码,耦合度极低。
实现步骤:
- 创建静态类封装附加属性:
using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using System; public static class CalendarViewExtensions { public static readonly DependencyProperty EnableWeekSelectionProperty = DependencyProperty.RegisterAttached("EnableWeekSelection", typeof(bool), typeof(CalendarViewExtensions), new PropertyMetadata(false, OnEnableWeekSelectionChanged)); public static bool GetEnableWeekSelection(CalendarView calendarView) { return (bool)calendarView.GetValue(EnableWeekSelectionProperty); } public static void SetEnableWeekSelection(CalendarView calendarView, bool value) { calendarView.SetValue(EnableWeekSelectionProperty, value); } private static void OnEnableWeekSelectionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { if (d is CalendarView calendarView) { if ((bool)e.NewValue) { calendarView.SelectedDatesChanged += OnCalendarViewSelectedDatesChanged; // 强制开启多选模式 calendarView.SelectionMode = CalendarViewSelectionMode.Multiple; } else { calendarView.SelectedDatesChanged -= OnCalendarViewSelectedDatesChanged; } } } private static void OnCalendarViewSelectedDatesChanged(CalendarView sender, CalendarViewSelectedDatesChangedEventArgs args) { // 只处理单个日期选中的场景,避免整周选中后重复触发 if (args.AddedDates.Count != 1) return; var selectedDate = args.AddedDates[0].Date; // 计算周起始(这里以周一为一周第一天,可根据需求改成周日) var firstDayOfWeek = selectedDate.AddDays(-(int)selectedDate.DayOfWeek + 1); var lastDayOfWeek = firstDayOfWeek.AddDays(6); // 清空现有选中项,避免重复选中 sender.SelectedDates.Clear(); // 添加整周所有日期 for (var date = firstDayOfWeek; date <= lastDayOfWeek; date = date.AddDays(1)) { sender.SelectedDates.Add(date); } } }
- 在XAML中直接启用该属性:
<CalendarView x:Name="MyCalendar" DisplayMode="Month" local:CalendarViewExtensions.EnableWeekSelection="True"/>
这样所有逻辑都封装在附加属性里,多个CalendarView可以直接复用,完全不用在页面后台写事件处理代码。
方案二:自定义样式实现视觉高亮(不修改选中集合)
如果你不需要实际选中整周日期,只是想视觉上高亮提示,这个方案更合适——保持SelectedDates的单一选中状态,通过样式绑定实现整周高亮。
实现步骤:
- ViewModel中添加周范围属性:
private DateTimeOffset _selectedWeekStart; public DateTimeOffset SelectedWeekStart { get => _selectedWeekStart; set => Set(ref _selectedWeekStart, value); } private DateTimeOffset _selectedWeekEnd; public DateTimeOffset SelectedWeekEnd { get => _selectedWeekEnd; set => Set(ref _selectedWeekEnd, value); } // 处理日期选中的方法 public void OnDateSelected(DateTimeOffset selectedDate) { var date = selectedDate.Date; SelectedWeekStart = date.AddDays(-(int)date.DayOfWeek + 1); SelectedWeekEnd = SelectedWeekStart.AddDays(6); }
- 自定义CalendarViewDayItem样式,添加高亮背景:
<Page.Resources> <local:DateInRangeConverter x:Key="DateInRangeConverter"/> </Page.Resources> <CalendarView DisplayMode="Month" SelectionMode="Single" SelectedDatesChanged="CalendarView_SelectedDatesChanged"> <CalendarView.CalendarViewDayItemStyle> <Style TargetType="CalendarViewDayItem"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="CalendarViewDayItem"> <Grid> <!-- 整周高亮背景 --> <Rectangle x:Name="WeekHighlightBg" Fill="#FFE5F1FF" Opacity="0" IsHitTestVisible="False"/> <!-- 原有控件内容 --> <ContentPresenter x:Name="ContentPresenter" Content="{TemplateBinding Content}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/> </Grid> <ControlTemplate.Triggers> <DataTrigger Binding="{Binding Date, RelativeSource={RelativeSource Self}, Converter={StaticResource DateInRangeConverter}, ConverterParameter={x:Bind ViewModel.SelectedWeekStart, Mode=OneWay}|{x:Bind ViewModel.SelectedWeekEnd, Mode=OneWay}}" Value="True"> <Setter TargetName="WeekHighlightBg" Property="Opacity" Value="1"/> </DataTrigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style> </CalendarView.CalendarViewDayItemStyle> </CalendarView>
- 实现日期范围判断转换器:
using Windows.UI.Xaml.Data; using System; public class DateInRangeConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, string language) { if (value is DateTimeOffset date && parameter is string rangeStr) { var rangeParts = rangeStr.Split('|'); if (rangeParts.Length == 2 && DateTimeOffset.TryParse(rangeParts[0], out var startDate) && DateTimeOffset.TryParse(rangeParts[1], out var endDate)) { return date.Date >= startDate.Date && date.Date <= endDate.Date; } } return false; } public object ConvertBack(object value, Type targetType, object parameter, string language) { throw new NotImplementedException(); } }
- 页面后台触发ViewModel更新:
private void CalendarView_SelectedDatesChanged(CalendarView sender, CalendarViewSelectedDatesChangedEventArgs args) { if (args.AddedDates.Count == 1) { ViewModel.OnDateSelected(args.AddedDates[0]); } }
这个方案的好处是不会破坏CalendarView的原生选中逻辑,只是做视觉增强,适合只需要高亮提示的场景。
总结
- 如果需要实际选中整周日期,方案一的附加属性是最省心的,逻辑封装性强,复用性高;
- 如果只是需要视觉高亮,方案二的样式绑定更合适,保持原生选中状态的同时实现整周提示。
这两个方案都能避免手动处理繁琐的事件添加/移除,大大降低出错概率,代码也更干净易维护。
内容的提问来源于stack exchange,提问作者Patric
相关产品推荐
相关产品推荐

