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