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

.NET MAUI如何滚动到指定元素?日历加载时聚焦当前日期

.NET MAUI 日历控件需求实现方案

一、实现显示上月1号和下月1号

修改日历数据生成逻辑,确保CalendarModel列表中包含上月1号、当月所有日期和下月1号,同时保持日历为完整的周结构(避免残缺行)。假设你的CalendarModel包含Date(存储具体日期)、IsCurrentMonth(标记是否为当前月)、IsCurrentDay(标记是否为今天)等属性,调整PopulateCalendar方法如下:

public async Task PopulateCalendar()
{
    CalendarModel.Clear(); // 假设CalendarModel是ObservableCollection<CalendarModel>

    int currentMonth = currentCalendar[0];
    int currentYear = currentCalendar[1];

    // 获取当前月、上月、下月的关键日期
    DateTime currentMonthFirst = new DateTime(currentYear, currentMonth, 1);
    DateTime currentMonthLast = currentMonthFirst.AddMonths(1).AddDays(-1);
    DateTime prevMonthFirst = currentMonthFirst.AddMonths(-1);
    DateTime nextMonthFirst = currentMonthLast.AddDays(1);

    // 计算日历起始日期:从当前月第一天的前一个周日(或周一,可自行调整)开始,若早于上月1号则从1号开始
    int daysBeforeCurrentMonth = (int)currentMonthFirst.DayOfWeek;
    DateTime startDate = currentMonthFirst.AddDays(-daysBeforeCurrentMonth);
    if (startDate < prevMonthFirst) startDate = prevMonthFirst;

    // 添加上月日期(从起始日到上月最后一天)
    for (DateTime date = startDate; date < currentMonthFirst; date = date.AddDays(1))
    {
        CalendarModel.Add(new CalendarModel
        {
            Date = date,
            DayName = date.ToString("ddd"),
            DayNumber = date.Day.ToString(),
            IsCurrentMonth = false,
            IsCurrentDay = date.Date == DateTime.Today.Date
        });
    }

    // 添加当前月所有日期
    for (DateTime date = currentMonthFirst; date <= currentMonthLast; date = date.AddDays(1))
    {
        CalendarModel.Add(new CalendarModel
        {
            Date = date,
            DayName = date.ToString("ddd"),
            DayNumber = date.Day.ToString(),
            IsCurrentMonth = true,
            IsCurrentDay = date.Date == DateTime.Today.Date
        });
    }

    // 计算日历结束日期:到当前月最后一天的下一个周六,确保包含下月1号
    int daysAfterCurrentMonth = 6 - (int)currentMonthLast.DayOfWeek;
    DateTime endDate = currentMonthLast.AddDays(daysAfterCurrentMonth);
    if (endDate < nextMonthFirst) endDate = nextMonthFirst;

    // 添加下月日期(从下月1号到结束日)
    for (DateTime date = nextMonthFirst; date <= endDate; date = date.AddDays(1))
    {
        CalendarModel.Add(new CalendarModel
        {
            Date = date,
            DayName = date.ToString("ddd"),
            DayNumber = date.Day.ToString(),
            IsCurrentMonth = false,
            IsCurrentDay = date.Date == DateTime.Today.Date
        });
    }
}

二、实现加载新月份时自动滚动到当前日期

方法1:代码后台事件绑定(简单直接)

  1. 在XAML中给CollectionView命名:
<CollectionView x:Name="CalendarCollectionView"
                ItemsSource="{Binding CalendarModel}"
                SelectionMode="None">
    <!-- 原有布局内容不变 -->
</CollectionView>
  1. 在ViewModel中添加数据加载完成事件:
public event EventHandler CalendarDataLoaded;

public async Task PopulateCalendar()
{
    // 原有数据生成逻辑...

    // 数据加载完成后触发事件
    CalendarDataLoaded?.Invoke(this, EventArgs.Empty);
}
  1. 在页面代码后台(.xaml.cs)订阅事件并执行滚动:
public YourCalendarPage()
{
    InitializeComponent();
    if (BindingContext is CalendarViewModel vm)
    {
        vm.CalendarDataLoaded += OnCalendarDataLoaded;
    }
}

private void OnCalendarDataLoaded(object sender, EventArgs e)
{
    if (BindingContext is CalendarViewModel vm)
    {
        // 找到当前日期对应的项
        var currentDayItem = vm.CalendarModel.FirstOrDefault(item => item.IsCurrentDay);
        if (currentDayItem != null)
        {
            // 滚动到当前日期,居中显示并带动画
            CalendarCollectionView.ScrollTo(currentDayItem, ScrollToPosition.Center, animate: true);
        }
    }
}

方法2:MVVM行为(符合MVVM规范)

  1. 创建自定义滚动行为:
using Microsoft.Maui.Controls;

public class ScrollToCurrentDayBehavior : Behavior<CollectionView>
{
    public static readonly BindableProperty CurrentDayItemProperty =
        BindableProperty.Create(nameof(CurrentDayItem), typeof(object), typeof(ScrollToCurrentDayBehavior), null, propertyChanged: OnCurrentDayItemChanged);

    public object CurrentDayItem
    {
        get => GetValue(CurrentDayItemProperty);
        set => SetValue(CurrentDayItemProperty, value);
    }

    private static void OnCurrentDayItemChanged(BindableObject bindable, object oldValue, object newValue)
    {
        if (bindable is ScrollToCurrentDayBehavior behavior && behavior.AssociatedObject != null && newValue != null)
        {
            behavior.AssociatedObject.ScrollTo(newValue, ScrollToPosition.Center, true);
        }
    }

    protected override void OnAttachedTo(CollectionView bindable)
    {
        base.OnAttachedTo(bindable);
    }

    protected override void OnDetachingFrom(CollectionView bindable)
    {
        base.OnDetachingFrom(bindable);
    }
}
  1. 在XAML中引用行为并绑定属性:
<CollectionView
    ItemsSource="{Binding CalendarModel}"
    SelectionMode="None">
    <CollectionView.Behaviors>
        <local:ScrollToCurrentDayBehavior CurrentDayItem="{Binding CurrentDayItem}" />
    </CollectionView.Behaviors>
    <!-- 原有布局内容不变 -->
</CollectionView>
  1. 在ViewModel中添加CurrentDayItem属性:
private CalendarModel _currentDayItem;
public CalendarModel CurrentDayItem
{
    get => _currentDayItem;
    set => SetProperty(ref _currentDayItem, value); // 依赖CommunityToolkit.Mvvm的ObservableObject
}

public async Task PopulateCalendar()
{
    // 原有数据生成逻辑...

    // 设置当前日期项,触发滚动
    CurrentDayItem = CalendarModel.FirstOrDefault(item => item.IsCurrentDay);
}

三、额外优化

如果需要仅在显示当前月份时滚动到今天,可在ViewModel中添加判断:

var today = DateTime.Today;
if (today.Month == currentCalendar[0] && today.Year == currentCalendar[1])
{
    CurrentDayItem = CalendarModel.FirstOrDefault(item => item.IsCurrentDay);
}
else
{
    // 切换到非当前月份时,滚动到当前显示月份的第一天
    CurrentDayItem = CalendarModel.FirstOrDefault(item => item.IsCurrentMonth && item.Date.Day == 1);
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 19:40:13