如何使用 wrapPanels 在日历中正确显示日期?

How to display days in calendar properly using wrapPanels?

我正在编写 WPF 应用程序。我有一个 CalendarButtons 用于上个月 (<)、当前 (今天) 和下个月 (>)。我的问题是,当我切换下一个月或上一个月时,每个月的第一天总是在同一天开始。

我有一个面板,我可以在其中创建其他面板。每个面板代表一天。我的代码应该在正确的位置放置一个带有天数的标签,但它总是从第一个 WrapPanel 开始。问题出在哪里?下面是一些屏幕截图。

单击下个月按钮后,二月应从星期四开始。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;

namespace ActivityMonitor
{
/// <summary>
/// Logika interakcji dla klasy CalendarWindow.xaml
/// </summary>
public partial class CalendarWindow : Window
{
    //lista dni w danym miesiacu
    private List<WrapPanel> daysList = new List<WrapPanel>();

   //aktualna data
    private DateTime currentDate = DateTime.Today;

    public CalendarWindow()
    {
        InitializeComponent();
        DisplayCalendar();
    }

    //metoda wyświetlająca kalendarz
    private void DisplayCalendar()  
    {
        GenerateDayPanel(42);
        //AddDayLabelToWrap(GetFirstDayOfCurrentDate(), GetTotalDaysOfCurrentDate());
        DisplayCurrentDate();
    }

    //metoda 
    private int GetFirstDayOfCurrentDate()
    {
        DateTime firstDayOfMonth = new DateTime(currentDate.Year, currentDate.Month, 1);
        return (int) firstDayOfMonth.DayOfWeek + 1;
    }

    private int GetTotalDaysOfCurrentDate()
    {
        DateTime firstDayOfCurrentDate = new DateTime(currentDate.Year, currentDate.Month, 1);
        return firstDayOfCurrentDate.AddMonths(1).AddDays(-1).Day;
    }

    private void DisplayCurrentDate()
    {
        labelMonthAndYear.Content = currentDate.ToString("MMMM, yyyy");
        AddDayLabelToWrap(GetFirstDayOfCurrentDate(), GetTotalDaysOfCurrentDate());

    }

    //metoda ustawuająca miesiąc na poprzedni
    private void PreviousMonth()
    {
        currentDate = currentDate.AddMonths(-1);
        DisplayCurrentDate();
    }

    //metoda ustawiająca miesiąc na następny
    private void NextMonth()
    {
        currentDate = currentDate.AddMonths(1);
        DisplayCurrentDate();
    }

    //metoda ustawiająca miesiąc na aktualny
    private void Today()
    {
        currentDate = DateTime.Today;
        DisplayCurrentDate();
    }

    //metoda generująca dni tygodnia danego miesiąca
    private void GenerateDayPanel(int totalDays)
    {
        daysPanel.Children.Clear();
        daysList.Clear();
        for (int i = 1; i <= totalDays; i++)
        {
            var wrap = new WrapPanel();
            wrap.Name = $"wrap{i}";
            wrap.ItemWidth = 200;
            wrap.ItemHeight = 100;
            if(i%2 == 0)
            {
                wrap.Background = new SolidColorBrush(Colors.LightBlue);
            }
            else
            {
                wrap.Background = new SolidColorBrush(Colors.Gray);
            }
            daysPanel.Children.Add(wrap);
            daysList.Add(wrap);
        }
    }

    //metoda dodająca labele z numerami dni miesiąca
    private void AddDayLabelToWrap(int startDayAtPanel, int totalDaysInMonth)
    {
        foreach (WrapPanel wrap in daysList)
        {
            wrap.Children.Clear();
        }
        for (int i = 1; i <= totalDaysInMonth; i++)
        {
            var lab = new Label();
            lab.Name = $"lblDay{i}";
            lab.Content = i;
            lab.HorizontalContentAlignment = HorizontalAlignment.Right;
            daysList[(i - 1) + (startDayAtPanel - 1)].Children.Add(lab);
        }
    }

    
    private void ButtonPrevMonth_Click(object sender, RoutedEventArgs e)
    {
        PreviousMonth();
    }

    private void ButtonNextMonth_Click(object sender, RoutedEventArgs e)
    {
        NextMonth();
    }

    private void ButtonToday_Click(object sender, RoutedEventArgs e)
    {
        Today();
    }

}
}

我做了你需要的样品,放在了GitHub。

Here.
https://github.com/ncoresoftsource/Whosebugsample/tree/main/src/answers/custom-calendar-app

这并不像我想的那么简单。和其他人的建议一样,把UI写在后面的代码中并不是很长运行的好方法,所以我给你做的这个示例源码希望你好好研究一下!

随便点!

  • 使用列表框

    public class CalendarBox : ListBox
    {
    
    }
    
  • 样式和模板

    <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                        xmlns:ctrl="clr-namespace:CalendarCore.Controls">
    
        <Style TargetType="{x:Type Label}" x:Key="LABEL.WEEK">
            <Setter Property="VerticalContentAlignment" Value="Center"/>
            <Setter Property="HorizontalContentAlignment" Value="Center"/>
            <Setter Property="BorderThickness" Value="0 0 1 1"/>
            <Setter Property="BorderBrush" Value="#DDDDDD"/>
            <Setter Property="Background" Value="#F1F1F1"/>
            <Setter Property="Padding" Value="0 4 0 4"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type Label}">
                        <Border Background="{TemplateBinding Background}"
                                BorderThickness="{TemplateBinding BorderThickness}"
                                BorderBrush="{TemplateBinding BorderBrush}"
                                Padding="{TemplateBinding Padding}">
                            <TextBlock Text="{TemplateBinding Content}" 
                                       VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                                       HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"/>
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    
        <Style TargetType="{x:Type ListBoxItem}" x:Key="LBXI.DAY">
            <Setter Property="Background" Value="Transparent"/>
            <Setter Property="BorderThickness" Value="0 0 1 1"/>
            <Setter Property="BorderBrush" Value="#DDDDDD"/>
            <Setter Property="Padding" Value="10"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type ListBoxItem}">
                        <Border Background="{TemplateBinding Background}" BorderThickness="{TemplateBinding BorderThickness}"
                                BorderBrush="{TemplateBinding BorderBrush}"
                                Padding="{TemplateBinding Padding}">
                            <TextBlock Text="{Binding Day}" />
                        </Border>
                        <ControlTemplate.Triggers>
                            <Trigger Property="ItemsControl.AlternationIndex" Value="0">
                                <Setter Property="Background" Value="#FAFAFA"/>
                            </Trigger>
                            <Trigger Property="ItemsControl.AlternationIndex" Value="1">
                                <Setter Property="Background" Value="#F1F1F1"/>
                            </Trigger>
                            <DataTrigger Binding="{Binding IsLastMonth}" Value="True">
                                <Setter Property="Foreground" Value="#BBBBBB"/>
                            </DataTrigger>
                            <DataTrigger Binding="{Binding IsNextMonth}" Value="True">
                                <Setter Property="Foreground" Value="#BBBBBB"/>
                            </DataTrigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    
        <Style TargetType="{x:Type ctrl:CalendarBox}">
            <Setter Property="AlternationCount" Value="2"/>
            <Setter Property="ItemContainerStyle" Value="{StaticResource LBXI.DAY}"/>
            <Setter Property="Margin" Value="10"/>
            <Setter Property="BorderThickness" Value="1 1 0 0"/>
            <Setter Property="BorderBrush" Value="#DDDDDD"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type ctrl:CalendarBox}">
                        <Border BorderThickness="{TemplateBinding BorderThickness}"
                                BorderBrush="{TemplateBinding BorderBrush}">
                            <Grid>
                                <Grid.RowDefinitions>
                                    <RowDefinition Height="Auto"/>
                                    <RowDefinition Height="*"/>
                                </Grid.RowDefinitions>
                                <UniformGrid Columns="7">
                                    <Label Style="{StaticResource LABEL.WEEK}" Content="MON" Background="#FFFFEAEA"/>
                                    <Label Style="{StaticResource LABEL.WEEK}" Content="TUE" Background="#FFE8F9FF"/>
                                    <Label Style="{StaticResource LABEL.WEEK}" Content="WED" Background="#FFE1F1C5"/>
                                    <Label Style="{StaticResource LABEL.WEEK}" Content="THU" Background="#FFFFD7D7"/>
                                    <Label Style="{StaticResource LABEL.WEEK}" Content="FRI" Background="#FFE9F9E4"/>
                                    <Label Style="{StaticResource LABEL.WEEK}" Content="SAT" Background="#FFF7F6E3"/>
                                    <Label Style="{StaticResource LABEL.WEEK}" Content="SUN" Background="#FFC4DAE4"/>
                                </UniformGrid>
    
                                <ItemsPresenter Grid.Row="1"/>
                            </Grid>
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
            <Setter Property="ItemsPanel">
                <Setter.Value>
                    <ItemsPanelTemplate>
                        <UniformGrid Columns="7"/>
                    </ItemsPanelTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </ResourceDictionary>
    
  • 主窗口 (.xaml)

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <StackPanel Grid.Row="0" Orientation="Horizontal" HorizontalAlignment="Right">
            <Button x:Name="btnPreview" Content="Preview" Margin="4" Padding="4"/>
            <Button x:Name="btnNext" Content="Next" Margin="4" Padding="4"/>
        </StackPanel>
        <ctrl:CalendarBox Grid.Row="1" x:Name="calendar"/>
    </Grid>
    
  • 隐藏代码 (.cs)

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Windows;
    using CalendarCore.Enums;
    using CalendarCore.Models;
    
    namespace CalendarDemo.Basic
    {
        public partial class MainWindow : Window
        {
            private int Year;
            private int Month;
    
            public MainWindow()
            {
                InitializeComponent();
    
                Year = DateTime.Now.Year;
                Month = DateTime.Now.Month;
    
                Loaded += (s,e)=> Refresh(CalendarMove.None);
                btnPreview.Click += (ps, pe) => Refresh(CalendarMove.Preview);
                btnNext.Click += (ns, ne) => Refresh(CalendarMove.Next);
            }
    
            private void Refresh(CalendarMove move)
            {
                DateTime currentDateTime = new DateTime(Year, Month, 1);
                int moveMonth = 0;
                switch (move)
                {
                    case CalendarMove.None: moveMonth = 0; break;
                    case CalendarMove.Preview: moveMonth = -1; break;
                    case CalendarMove.Next: moveMonth = 1; break;
                }
    
                Year = currentDateTime.AddMonths(moveMonth).Year; 
                Month = currentDateTime.AddMonths(moveMonth).Month;
    
                calendar.ItemsSource = GenerateCalendar(Year, Month);
            }
    
            private IEnumerable GenerateCalendar(int year, int month)
            {
                List<DayModel> days = new List<DayModel>();
    
                // Step 1. Add days of last month.
                AddDaysOfLastMonth(year, month, ref days);
    
                // Step 2. Add days of current mon.th
                AddDaysOfCurrentMonth(year, month, ref days);
    
                // Step 3. Add days of next month.
                AddDaysOfNextMonth(year, month, ref days);
                return days;
            }
    
            private void AddDaysOfLastMonth(int year, int month, ref List<DayModel> days)
            {
                var lastMonth = new DateTime(year, month, 1).AddMonths(-1);
    
                int dayStarting;
                int lastDayOfLastMonth = DateTime.DaysInMonth(lastMonth.Year, lastMonth.Month);
                DayOfWeek firstDayOfWeek = new DateTime(year, month, 1).DayOfWeek;
    
                switch (firstDayOfWeek)
                {
                    case DayOfWeek.Monday: dayStarting = 0; break;
                    case DayOfWeek.Tuesday: dayStarting = 1; break;
                    case DayOfWeek.Wednesday: dayStarting = 2; break;
                    case DayOfWeek.Thursday: dayStarting = 3; break;
                    case DayOfWeek.Friday: dayStarting = 4; break;
                    case DayOfWeek.Saturday: dayStarting = 5; break;
                    case DayOfWeek.Sunday: dayStarting = 6; break;
                    default: dayStarting = 0;break;
                }
    
                for (int i = 1; i <= dayStarting; i++)
                {
                    days.Add(new DayModel
                    {
                        Date = new DateTime(lastMonth.Year, lastMonth.Month, lastDayOfLastMonth + i - dayStarting),
                        IsLastMonth = true
                    });
                }
            }
    
            private void AddDaysOfCurrentMonth(int year, int month, ref List<DayModel> days)
            {
                int lastDay = DateTime.DaysInMonth(year, month);
                for (int i = 1; i <= lastDay; i++)
                {
                    days.Add(new DayModel { Date = new DateTime(year, month, i) });
                }
            }
    
            private void AddDaysOfNextMonth(int year, int month, ref List<DayModel> days)
            {
                var nextMonth = new DateTime(year, month, 1).AddMonths(1);
                var lastDayofCurrentMonth = DateTime.DaysInMonth(year, month);
    
                int dayStarting;
                DayOfWeek lastDayOfWeek = new DateTime(year, month, lastDayofCurrentMonth).DayOfWeek;
    
                switch (lastDayOfWeek)
                {
                    case DayOfWeek.Monday: dayStarting = 6; break;
                    case DayOfWeek.Tuesday: dayStarting = 5; break;
                    case DayOfWeek.Wednesday: dayStarting = 4; break;
                    case DayOfWeek.Thursday: dayStarting = 3; break;
                    case DayOfWeek.Friday: dayStarting = 2; break;
                    case DayOfWeek.Saturday: dayStarting = 1; break;
                    case DayOfWeek.Sunday: dayStarting = 0; break;
                    default: dayStarting = 0; break;
                }
    
                for (int i = 1; i <= dayStarting; i++)
                {
                    days.Add(new DayModel
                    {
                        Date = new DateTime(nextMonth.Year, nextMonth.Month, i),
                        IsNextMonth = true
                    });
                }
            }
        }
    }
    
  • 型号

    public class DayModel
    {
        public DateTime Date { get; set; }
    
        public int Year => Date.Year;
        public int Month => Date.Month;
        public int Day => Date.Day;
    
        public bool IsLastMonth { get; set; }
        public bool IsCurrentMonth { get; set; }
        public bool IsNextMonth { get; set; }
    }