C# 用C编程调整网格中的行数#

C# 用C编程调整网格中的行数#,c#,wpf,xaml,C#,Wpf,Xaml,我目前正在自学XAML/C,并编写一个日历应用程序。现在它正在创建一个网格,然后将用户控件元素应用到网格。它正确地构建了我的日历,但我不想在XAML中定义行数,而是希望能够通过C#动态设置行数。有些月份使用的周数或多或少(3月需要5周,但4月需要6周)。我想知道如何做到这一点,或者我是否应该使用不同于网格的控件 XAML代码 <UserControl x:Class="CMS.Control.MonthView" xmlns="http://schemas.microsoft.c

我目前正在自学XAML/C,并编写一个日历应用程序。现在它正在创建一个网格,然后将用户控件元素应用到网格。它正确地构建了我的日历,但我不想在XAML中定义行数,而是希望能够通过C#动态设置行数。有些月份使用的周数或多或少(3月需要5周,但4月需要6周)。我想知道如何做到这一点,或者我是否应该使用不同于网格的控件

XAML代码

<UserControl x:Class="CMS.Control.MonthView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    HorizontalAlignment="Stretch" VerticalAlignment="Stretch" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch">

    <Grid VerticalAlignment="Stretch">
        <Grid.RowDefinitions>
            <RowDefinition Height="40"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <StackPanel Orientation="Horizontal" Background="AliceBlue">
            <Image x:Name="MonthPrev" Source="/Images/Previous.png" Height="24" Margin="16,0,6,0" MouseLeftButtonUp="MonthPrev_MouseLeftButtonUp"/>
            <Image x:Name="MonthNext" Source="/Images/Next.png" Height="24" Margin="6,0,16,0" MouseLeftButtonUp="MonthNext_MouseLeftButtonUp"/>
            <Label x:Name="DateLabel" Content="January 2017" FontSize="16" FontFamily="Bold" VerticalAlignment="Center"/>
        </StackPanel>
        <Grid Grid.Row="1" Background="AliceBlue">
            <Grid.ColumnDefinitions>

                <ColumnDefinition MinWidth="60" Width="*"/>
                <ColumnDefinition MinWidth="60" Width="*"/>
                <ColumnDefinition MinWidth="60" Width="*"/>
                <ColumnDefinition MinWidth="60" Width="*"/>
                <ColumnDefinition MinWidth="60" Width="*"/>
                <ColumnDefinition MinWidth="60" Width="*"/>
                <ColumnDefinition MinWidth="60" Width="*"/>
            </Grid.ColumnDefinitions>
            <Label Grid.Column="0" Content="Sunday" FontSize="9" Margin="2,0,0,2" Padding="0,1,0,0" HorizontalAlignment="Center" VerticalAlignment="Center" BorderThickness="0,0,1,0"/>
            <Label Grid.Column="1" Content="Monday" FontSize="9" Margin="2,0,0,2" Padding="0,1,0,0" HorizontalAlignment="Center" VerticalAlignment="Center" BorderThickness="0,0,1,0"/>
            <Label Grid.Column="2" Content="Tuesday" FontSize="9" Margin="2,0,0,2" Padding="0,1,0,0" HorizontalAlignment="Center" VerticalAlignment="Center" BorderThickness="0,0,1,0"/>
            <Label Grid.Column="3" Content="Wednesday" FontSize="9" Margin="2,0,0,2" Padding="0,1,0,0" HorizontalAlignment="Center" VerticalAlignment="Center" BorderThickness="0,0,1,0"/>
            <Label Grid.Column="4" Content="Thursday" FontSize="9" Margin="2,0,0,2" Padding="0,1,0,0" HorizontalAlignment="Center" VerticalAlignment="Center" BorderThickness="0,0,1,0"/>
            <Label Grid.Column="5" Content="Friday" FontSize="9" Margin="2,0,0,2" Padding="0,1,0,0" HorizontalAlignment="Center" VerticalAlignment="Center" BorderThickness="0,0,1,0"/>
            <Label Grid.Column="6" Content="Saturday" FontSize="9" Margin="2,0,0,2" Padding="0,1,0,0" HorizontalAlignment="Center" VerticalAlignment="Center" BorderThickness="0,0,1,0"/>
        </Grid>

        <Grid x:Name="WeekRowGrid" Grid.Row="2">
            <Grid.ColumnDefinitions>

                    <ColumnDefinition Width="*"/>
                    <ColumnDefinition Width="*"/>
                    <ColumnDefinition Width="*"/>
                    <ColumnDefinition Width="*"/>
                    <ColumnDefinition Width="*"/>
                    <ColumnDefinition Width="*"/>
                    <ColumnDefinition Width="*"/>
                </Grid.ColumnDefinitions>

            <Grid.RowDefinitions>
                <RowDefinition Height="*"/>
                <RowDefinition Height="*"/>
                <RowDefinition Height="*"/>
                <RowDefinition Height="*"/>
                <RowDefinition Height="*"/>
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>
        </Grid>
    </Grid>
</UserControl>

C#代码

namespace CMS.Control
{
/// 
///MonthView.xaml的交互逻辑
/// 
公共部分类MonthView:UserControl
{
私有日期时间_DispayDate;
公共MonthView()
{
初始化组件();
_DispayDate=DateTime.Now;
提款月份();
}
//生成
私人月
{
DateTime FirstDayOfMonth=新的日期时间(_DispayDate.Year,_DispayDate.Month,1);
int DisplayFrontPadding=(int)FirstDayOfMonth.DayOfWeek;//#在该月1日之前需要显示的天数
int DaysInDisplayMonth=DateTime.DaysInMonth(_DispayDate.Year,_DispayDate.Month);
int DaysInDisplay=DisplayFrontPadding+DaysInDisplayMonth;
DaysInDisplay+=7-DaysInDisplay%7;//将显示的天数取整为7的倍数
DateLabel.Content=\u DispayDate.ToString(“MMMM”)+“”+\u DispayDate.Year;

对于(int i=0;i您可以创建一个方法,根据周数自动调整行数。此方法始终删除所有行,然后添加所需的正确行数

private void AdjustRowDefinitions(int numberOfWeeks)
{
    WeekRowGrid.RowDefinitions.Clear();
    for (int i = 0; i < numberOfWeeks; i++)
    {
        RowDefinition rowDef = new RowDefinition();
        rowDef.Height = new GridLength(1, GridUnitType.Star); //this sets the height of the row to *
        WeekRowGrid.RowDefinitions.Add(rowDef);
    }
}
private void AdjustRowDefinitions(整数周)
{
WeekRowGrid.RowDefinitions.Clear();
对于(int i=0;i
我只是想完成这个项目

理解。问题是,MVVM背后的基本思想其实并不难,如果你接受它,你可能会比继续硬编码所有UI更快地完成项目。当然,我不能保证这一点。但我也经历过同样的事情,我可以告诉你,你可以花很多时间与WPF进行斗争在代码隐藏中显式配置UI

如果没有一个好的开始,对我来说完全复制您的用户界面是不现实的。但是下面是一个简单的代码示例,它展示了使用MVVM构建您想要的UI的基本方法

首先,为您提供一个实现
INotifyPropertyChanged
的基类很有帮助。它大大简化了视图模型样板:

class NotifyPropertyChangedBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected void _UpdateField<T>(ref T field, T newValue, [CallerMemberName] string propertyName = null)
    {
        if (!EqualityComparer<T>.Default.Equals(field, newValue))
        {
            field = newValue;
            _OnPropertyChanged(propertyName);
        }
    }

    protected virtual void _OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}
而且

class MonthViewViewModel : NotifyPropertyChangedBase
{
    private readonly ObservableCollection<DateViewModel> _dates = new ObservableCollection<DateViewModel>();

    private DateTime _selectedDate;

    public DateTime SelectedDate
    {
        get { return _selectedDate; }
        set { _UpdateField(ref _selectedDate, value); }
    }

    public IReadOnlyCollection<DateViewModel> Dates
    {
        get { return _dates; }
    }

    protected override void _OnPropertyChanged(string propertyName)
    {
        base._OnPropertyChanged(propertyName);

        switch (propertyName)
        {
            case nameof(SelectedDate):
                _UpdateDates();
                break;
        }
    }

    private void _UpdateDates()
    {
        _dates.Clear();

        DateTime firstDayOfMonth = new DateTime(SelectedDate.Year, SelectedDate.Month, 1),
            firstDayOfNextMonth = firstDayOfMonth.AddMonths(1);
        int previousMonthDates = (int)firstDayOfMonth.DayOfWeek; // assumes Sunday-start week
        int daysInView = previousMonthDates + DateTime.DaysInMonth(SelectedDate.Year, SelectedDate.Month);

        // round up to nearest week multiple
        daysInView = ((daysInView - 1) / 7 + 1) * 7;

        DateTime previousMonth = firstDayOfMonth.AddDays(-previousMonthDates);

        for (DateTime date = previousMonth; date < firstDayOfNextMonth; date = date.AddDays(1))
        {
            _dates.Add(new DateViewModel { DayNumber = date.Day, IsCurrent = date == SelectedDate.Date });
        }

        for (int i = 1; _dates.Count < daysInView; i++)
        {
            _dates.Add(new DateViewModel { DayNumber = i, IsCurrent = false });
        }
    }
}
XAML做了三件事:

  • 它声明了一个
    MonthViewViewModel
    对象,用作
    窗口的
    DataContext
    。可视化树中的对象,即
    窗口的子对象,如果没有自己的上下文,将继承父对象的上下文
  • 它为两个视图模型声明数据模板。这些模板告诉WPF它应该如何直观地表示数据。视图模型包含要表示的数据,并且该数据通过
    {Binding…}
    语法在模板中引用。在许多情况下(例如,文本、数字、枚举值),您可以直接绑定,默认转换将执行您想要的操作(如上所述)。如果没有,您可以实现自己的
    IValueConverter
    ,并将其合并到绑定中
  • 它通过声明一个
    ContentControl
    ,提供了一个显示
    MonthViewViewModel
    的位置,其中该控件的内容仅绑定到当前数据上下文(没有路径的
    {Binding}
    表达式将绑定到源,默认源是当前数据上下文)
  • ContentControl
    的上下文中,以及在
    ItemsControl
    中显示的各个项目中,WPF将搜索适用于为该上下文定义的数据对象的模板,并将根据该对象自动填充绑定到必要属性的可视化树

    这种方法有许多优点,主要的优点是您可以描述您的UI,而不必对其进行编码,并且您可以维护“关注点分离”的OOP原则,这是减少脑力劳动负担的关键,它允许您一次只关注一件事,而不必同时处理UI和数据逻辑

    关于上述XAML的几个旁注:

    • 您可能会注意到,我添加了
      p:
      XML名称空间,并将其用于
      Style
      元素。这只是为了解决堆栈溢出错误,其中
      Style
      元素本身会混淆XML格式化程序,并阻止元素及其子元素正确格式化
      class DateViewModel : NotifyPropertyChangedBase
      {
          private int _dayNumber;
          private bool _isCurrent;
      
          public int DayNumber
          {
              get { return _dayNumber; }
              set { _UpdateField(ref _dayNumber, value); }
          }
      
          public bool IsCurrent
          {
              get { return _isCurrent; }
              set { _UpdateField(ref _isCurrent, value); }
          }
      }
      
      class MonthViewViewModel : NotifyPropertyChangedBase
      {
          private readonly ObservableCollection<DateViewModel> _dates = new ObservableCollection<DateViewModel>();
      
          private DateTime _selectedDate;
      
          public DateTime SelectedDate
          {
              get { return _selectedDate; }
              set { _UpdateField(ref _selectedDate, value); }
          }
      
          public IReadOnlyCollection<DateViewModel> Dates
          {
              get { return _dates; }
          }
      
          protected override void _OnPropertyChanged(string propertyName)
          {
              base._OnPropertyChanged(propertyName);
      
              switch (propertyName)
              {
                  case nameof(SelectedDate):
                      _UpdateDates();
                      break;
              }
          }
      
          private void _UpdateDates()
          {
              _dates.Clear();
      
              DateTime firstDayOfMonth = new DateTime(SelectedDate.Year, SelectedDate.Month, 1),
                  firstDayOfNextMonth = firstDayOfMonth.AddMonths(1);
              int previousMonthDates = (int)firstDayOfMonth.DayOfWeek; // assumes Sunday-start week
              int daysInView = previousMonthDates + DateTime.DaysInMonth(SelectedDate.Year, SelectedDate.Month);
      
              // round up to nearest week multiple
              daysInView = ((daysInView - 1) / 7 + 1) * 7;
      
              DateTime previousMonth = firstDayOfMonth.AddDays(-previousMonthDates);
      
              for (DateTime date = previousMonth; date < firstDayOfNextMonth; date = date.AddDays(1))
              {
                  _dates.Add(new DateViewModel { DayNumber = date.Day, IsCurrent = date == SelectedDate.Date });
              }
      
              for (int i = 1; _dates.Count < daysInView; i++)
              {
                  _dates.Add(new DateViewModel { DayNumber = i, IsCurrent = false });
              }
          }
      }
      
      <Window x:Class="TestSO43147585CalendarMonthView.MainWindow"
              xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
              xmlns:p="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
              xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
              xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
              xmlns:l="clr-namespace:TestSO43147585CalendarMonthView"
              xmlns:s="clr-namespace:System;assembly=mscorlib"
              mc:Ignorable="d"
              SizeToContent="Height"
              Title="MainWindow" Height="350" Width="525">
        <Window.DataContext>
          <l:MonthViewViewModel SelectedDate="{x:Static s:DateTime.Today}"/>
        </Window.DataContext>
      
        <Window.Resources>
          <DataTemplate DataType="{x:Type l:MonthViewViewModel}">
            <ItemsControl ItemsSource="{Binding Dates}">
              <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                  <UniformGrid IsItemsHost="True" Columns="7"/>
                </ItemsPanelTemplate>
              </ItemsControl.ItemsPanel>
            </ItemsControl>
          </DataTemplate>
      
          <DataTemplate DataType="{x:Type l:DateViewModel}">
            <Border BorderBrush="Black" BorderThickness="0, 0, 1, 0">
              <StackPanel>
                <TextBlock Text="{Binding DayNumber}">
                  <TextBlock.Style>
                    <p:Style TargetType="TextBlock">
                      <Setter Property="Background" Value="LightBlue"/>
                      <p:Style.Triggers>
                        <DataTrigger Binding="{Binding IsCurrent}" Value="True">
                          <Setter Property="Background" Value="Yellow"/>
                        </DataTrigger>
                      </p:Style.Triggers>
                    </p:Style>
                  </TextBlock.Style>
                </TextBlock>
                <Grid Height="{Binding ActualWidth, RelativeSource={x:Static RelativeSource.Self}}"/>
              </StackPanel>
            </Border>
          </DataTemplate>
        </Window.Resources>
      
        <Grid>
          <ContentControl Content="{Binding}" VerticalAlignment="Top"/>
        </Grid>
      </Window>