You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

UWP开发:实现CalendarView选中日期时自动高亮整周

更优雅的UWP CalendarView整周高亮方案

我之前做UWP项目时也碰到过这个头疼的问题——手动绑定SelectedDatesChanged事件来维护整周选中状态,不仅代码冗余,还特别容易在月份切换、重复点击时出现状态错乱。这里有两个更简洁的方案,帮你摆脱繁琐的事件处理:

方案一:用附加属性封装整周选择逻辑

这种方式把整周选择的逻辑完全封装成一个可复用的附加属性,页面后台不用写任何事件代码,耦合度极低。

实现步骤:

  1. 创建静态类封装附加属性:
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);
        }
    }
}
  1. 在XAML中直接启用该属性:
<CalendarView x:Name="MyCalendar"
              DisplayMode="Month"
              local:CalendarViewExtensions.EnableWeekSelection="True"/>

这样所有逻辑都封装在附加属性里,多个CalendarView可以直接复用,完全不用在页面后台写事件处理代码。

方案二:自定义样式实现视觉高亮(不修改选中集合)

如果你不需要实际选中整周日期,只是想视觉上高亮提示,这个方案更合适——保持SelectedDates的单一选中状态,通过样式绑定实现整周高亮。

实现步骤:

  1. 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);
}
  1. 自定义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>
  1. 实现日期范围判断转换器:
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();
    }
}
  1. 页面后台触发ViewModel更新:
private void CalendarView_SelectedDatesChanged(CalendarView sender, CalendarViewSelectedDatesChangedEventArgs args)
{
    if (args.AddedDates.Count == 1)
    {
        ViewModel.OnDateSelected(args.AddedDates[0]);
    }
}

这个方案的好处是不会破坏CalendarView的原生选中逻辑,只是做视觉增强,适合只需要高亮提示的场景。

总结

  • 如果需要实际选中整周日期,方案一的附加属性是最省心的,逻辑封装性强,复用性高;
  • 如果只是需要视觉高亮,方案二的样式绑定更合适,保持原生选中状态的同时实现整周提示。

这两个方案都能避免手动处理繁琐的事件添加/移除,大大降低出错概率,代码也更干净易维护。

内容的提问来源于stack exchange,提问作者Patric

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 03:36:36