WPF(DataGid学习)
先看两张图,一个实现自己的效果,一个使用默认的效果:
GitHub地址 https://github.com/Bridge2018/WPF-DataGridhttps://github.com/Bridge2018/WPF-DataGrid
不多说,上代码。。。
1.转换器
因为里面有个DateTime,要通过转换器转换为对应的字符串。
using System;
using System.Globalization;
using System.Windows.Data;
[ValueConversion(typeof(DateTime), typeof(string))]
public class DateConverter : IValueConverter
{
public string Format { get; set; } = "yyyy-MM-dd HH:mm:ss.fff";
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
DateTime dt = (DateTime)value;
return dt.ToString(Format);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return DateTime.Parse(value.ToString());
}
}
2.资源字典
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApp6">
<FontFamily x:Key="iconFont">/WpfApp6;component/fonts/iconfont/#iconfont</FontFamily>
<FontFamily x:Key="songti">宋体</FontFamily>
<SolidColorBrush x:Key="mainBackground" Color="#B0E2FF" />
<local:DateConverter x:Key="dataConverterFull" />
<local:DateConverter x:Key="dataConverterShort" Format="yyyy-MM-dd HH:mm:ss" />
</ResourceDictionary>
为了实现两个CheckBox的效果,用到了iconFont图标字体 https://www.iconfont.cn/collections/index?spm=a313x.7781069.1998910419.5&type=1https://www.iconfont.cn/collections/index?spm=a313x.7781069.1998910419.5&type=1
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/WpfApp6;component/DictionaryUse.xaml" />
</ResourceDictionary.MergedDictionaries>
<Style TargetType="CheckBox" x:Key="ckColumn">
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="VerticalAlignment" Value="Stretch" />
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="FontFamily" Value="{StaticResource iconFont}" />
<Setter Property="FontSize" Value="25" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="CheckBox">
<Border Background="Transparent">
<TextBlock x:Name="tb" HorizontalAlignment="Center" VerticalAlignment="Center" FontFamily="{TemplateBinding FontFamily}" FontSize="{TemplateBinding FontSize}" FontWeight="{TemplateBinding FontWeight}" Foreground="Black" Text="{TemplateBinding Content}" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter TargetName="tb" Property="Text" Value="{Binding Tag, RelativeSource={RelativeSource AncestorType=CheckBox}}" />
<Setter TargetName="tb" Property="Foreground" Value="{Binding Foreground, RelativeSource={RelativeSource AncestorType=CheckBox}}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="TextBlock" x:Key="tbColumn">
<Setter Property="HorizontalAlignment" Value="Center" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="TextWrapping" Value="WrapWithOverflow" />
</Style>
<Style TargetType="DataGridColumnHeader" x:Key="gridColHeaderStyle">
<Setter Property="Background" Value="#4F94CD" />
<Setter Property="FontFamily" Value="{StaticResource songti}" />
<Setter Property="FontSize" Value="18" />
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="Padding" Value="8" />
<Setter Property="Foreground" Value="White" />
<Setter Property="TextBlock.TextWrapping" Value="Wrap" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="DataGridColumnHeader">
<Border Padding="{TemplateBinding Padding}" Background="{TemplateBinding Background}">
<TextBlock HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" Text="{TemplateBinding Content}" TextAlignment="Center" TextWrapping="Wrap" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="DataGridCell" x:Key="gridCellStyle">
<Setter Property="VerticalAlignment" Value="Stretch" />
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="#FFDEAD" />
<Setter Property="Foreground" Value="Black" />
<Setter Property="BorderThickness" Value="0" />
</Trigger>
</Style.Triggers>
</Style>
<Style TargetType="DataGridRow" x:Key="gridRowStyle">
<Setter Property="Background" Value="White" />
<Setter Property="Height" Value="35" />
<Setter Property="FontSize" Value="18" />
<!--<Setter Property="BorderThickness" Value="2"/>
<Setter Property="BorderBrush" Value="Red"/>-->
<Style.Triggers>
<Trigger Property="AlternationIndex" Value="1">
<Setter Property="Background" Value="#87CEFF" />
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#EEA9B8" />
<Setter Property="BorderThickness" Value="0,2,0,2" />
<Setter Property="BorderBrush" Value="Black" />
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="FontSize" To="20" Duration="0:0:0.3" />
<DoubleAnimation Storyboard.TargetProperty="Height" To="37" Duration="0:0:0.3" />
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="FontSize" Duration="0:0:0.2" />
<DoubleAnimation Storyboard.TargetProperty="Height" Duration="0:0:0.3" />
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
</Trigger>
</Style.Triggers>
</Style>
<Style TargetType="DataGrid" x:Key="gridStyle">
<!--<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="VerticalContentAlignment" Value="Center" />-->
<Setter Property="AlternationCount" Value="2" />
<Setter Property="AutoGenerateColumns" Value="False" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="CanUserAddRows" Value="False" />
<Setter Property="CanUserReorderColumns" Value="False" />
<Setter Property="CanUserResizeColumns" Value="False" />
<Setter Property="CanUserResizeRows" Value="False" />
<Setter Property="CanUserSortColumns" Value="False" />
<Setter Property="GridLinesVisibility" Value="None" />
<!--<Setter Property="VirtualizingPanel.IsVirtualizing" Value="False" />-->
<Setter Property="RowHeaderWidth" Value="0" />
<Setter Property="ColumnHeaderStyle" Value="{StaticResource gridColHeaderStyle}" />
<Setter Property="CellStyle" Value="{StaticResource gridCellStyle}" />
<Setter Property="RowStyle" Value="{StaticResource gridRowStyle}" />
</Style>
</ResourceDictionary>
3.视图模型
namespace WpfApp6
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections.ObjectModel;
using System.ComponentModel;
public class ProgramListViewModel
{
public ObservableCollection<ProgramStepModel> ProgramModels { get; set; } = new ObservableCollection<ProgramStepModel>();
public bool CanEdit { get; set; } = true;
public ProgramListViewModel()
{
Random random = new Random();
for (int i = 0; i < 30; i++)
{
ProgramModels.Add(new ProgramStepModel { IsQuick = random.Next(2) == 0, Num = i + 1, Name = Tools.RandomName(5, 8), AlterTime = DateTime.Now, IsLock = random.Next(2) == 0 });
}
}
}
public class ProgramStepModel
{
public bool IsQuick { get; set; }
public int Num { get; set; }
public string Name { get; set; }
public Sex Sex { get; set; }
public DateTime AlterTime { get; set; }
public bool IsLock { get; set; }
public override string ToString()
{
return $"IsQuick:{IsQuick} Num:{Num} Name:{Name} AlterTime:{AlterTime} IsLock:{IsLock}";
}
}
public enum Sex
{
Man,
Woman,
}
public class Tools
{
static Random Random = new Random();
public static string RandomName(int minLen, int maxLen, bool haveNum = true)
{
List<int> listAscii = new List<int>();
if (haveNum)
{
listAscii.AddRange(Enumerable.Range(48, 10));
}
listAscii.AddRange(Enumerable.Range(65, 26));
listAscii.AddRange(Enumerable.Range(97, 26));
return Encoding.ASCII.GetString(listAscii.ConvertAll(t => (byte)t).OrderBy(l => Guid.NewGuid()).Take(Random.Next(minLen, maxLen)).ToArray());
}
}
}
4.窗体XAML
<Window x:Class="WpfApp6.MainWindow" Title="MainWindow"
xmlns="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:local="clr-namespace:WpfApp6"
xmlns:core="clr-namespace:System;assembly=mscorlib"
Width="1000" Height="600" mc:Ignorable="d">
<Window.DataContext>
<local:ProgramListViewModel />
</Window.DataContext>
<Grid>
<Grid.Resources>
<ObjectDataProvider x:Key="myEnum" MethodName="GetValues" ObjectType="{x:Type core:Enum}">
<ObjectDataProvider.MethodParameters>
<x:Type Type="local:Sex" />
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
</Grid.Resources>
<Grid.ColumnDefinitions>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
</Grid.RowDefinitions>
<DataGrid x:Name="grid" AutoGenerateColumns="False" CanUserAddRows="True" ItemsSource="{Binding ProgramModels}" Style="{StaticResource gridStyle}">
<DataGrid.Columns>
<DataGridTemplateColumn Width="0.1*" Header="快捷" IsReadOnly="True">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox Content="" Foreground="#00B2EE" IsChecked="{Binding IsQuick, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding DataContext.CanEdit, RelativeSource={RelativeSource AncestorType=Window}}" Style="{StaticResource ckColumn}" Tag="" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Width="0.1*" Binding="{Binding Num}" ElementStyle="{StaticResource tbColumn}" Header="序号" IsReadOnly="True" />
<DataGridTextColumn Width="0.3*" Binding="{Binding Name}" ElementStyle="{StaticResource tbColumn}" Header="名称" />
<DataGridComboBoxColumn Width="0.1*" Header="性别" ItemsSource="{Binding Source={StaticResource myEnum}}" SelectedItemBinding="{Binding Sex}">
<DataGridComboBoxColumn.ElementStyle>
<Style TargetType="ComboBox">
<Setter Property="HorizontalAlignment" Value="Center" />
<Setter Property="VerticalAlignment" Value="Center" />
</Style>
</DataGridComboBoxColumn.ElementStyle>
</DataGridComboBoxColumn>
<DataGridTextColumn Width="0.2*" Binding="{Binding AlterTime, Converter={StaticResource dataConverterShort}}" ElementStyle="{StaticResource tbColumn}" Header="修改时间" IsReadOnly="True" />
<DataGridTemplateColumn Width="0.1*" Header="锁定" IsReadOnly="True">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox Content="" FontSize="28" FontWeight="Bold" Foreground="#EE7600" IsChecked="{Binding IsLock, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding DataContext.CanEdit, RelativeSource={RelativeSource AncestorType=Window}}" Style="{StaticResource ckColumn}" Tag="" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
5.DataGrid属性
- AutoGenerateColumns 获取或设置一个值,该值指示是否自动创建列。当设置为True时(默认为True),会根据ItemsSource自动创建列;若同时手动创建时,先显示自动创建的列再显示手动创建的列。
- AlternationCount 获取或设置 ItemsControl 中的交替项容器的数目,该控件可使交替容器具有唯一外观。例如:若是设置为3,那么每行的AlternationIndex就是按照 0,1,2,0,1,2...这样排序,我们就可以通过触发器设置对应行的样式了。
- GridLinesVisibility 指示显示哪些网格线,可以不显示,或者显示竖线、横线
- HeadersVisibility 指定行和列标题的可见性的值。
- EnableRowVirtualization、EnableColumnVirtualization 是否启用虚拟化,默认是启动的,仅在需要时才会创建对象,并尽可能多地重用它们。启用后若是获取未正在显示的行会出异常。
- DataGrid目前支持下面这集中类型的列:
DataGridTextColumn 文本类型,在TextBlock元素中显示 DataGridCheckBoxColumn 复选框 DataGridHyperlinkColumn 可单击的链接 DataGridComboxBox 可下拉的ComboBox控件 DataGridTemplateColumn 这种列允许自定义数据模板 -
每个列都有ElementStyle(未处于编辑模式的单元格显示的样式)和EditingElementStyle(呈现的列将显示处于编辑模式下的样式)。
-
只有在编辑模式下修改数据才有效,其他几个类型的列在非编辑模式下是无法修改数据的,但是DataGridCheckBoxColumn可以在非编辑模式下修改,只是显示效果上而已,并不能真正意义上改变了数据,所以当我们设计时要注意这一点。
-
可以通过设置每列的IsReadOnly来控制列信息是否可以修改,但是对于DataGridTemplateColumn无效,需要自行定义变量控制是否可以修改
6.DataGrid样式
通过上面的资源字典可以发现,这里我只对DataGrid中的三个属性设置了对应的样式:
<Setter Property="ColumnHeaderStyle" Value="{StaticResource gridColHeaderStyle}" />
<Setter Property="CellStyle" Value="{StaticResource gridCellStyle}" />
<Setter Property="RowStyle" Value="{StaticResource gridRowStyle}" />
RowHeaderStyle | 获取或设置应用于所有行标题的样式。TargetType="DataGridRowHeader" |
ColumnHeaderStyle | 获取或设置应用于中的所有列标题的样式 TargetType="DataGridColumnHeader" |
RowStyle | 获取或设置应用到的所有行的样式。TargetType="DataGridRow" |
CellStyle | 获取或设置应用于中的所有单元格的样式 TargetType="DataGridCell" |
DropLocationIndicatorStyle | 获取或设置用于指示何时拖动列标题的放置位置的样式。 |
DragIndicatorStyle | 获取或设置呈现拖动指示器显示同时拖动列标题时使用的样式。 |
7.DataGrid相关事件
private void grid_LoadingRow(object sender, DataGridRowEventArgs e)
{
DataGrid dataGrid = sender as DataGrid;
DataGridRow row = e.Row;
FrameworkElement cellContent = dataGrid.Columns[0].GetCellContent(row);
if (cellContent != null)
{
ProgramStepModel stepModel = row.Item as ProgramStepModel;
if (stepModel.IsLock)
{
DataGridCell gridCell = dataGrid.Columns[5].GetCellContent(row.Item).Parent as DataGridCell;
gridCell.Background = new SolidColorBrush(Colors.Red);
}
}
}
当我们如上述这样订阅事件,当IsLock为True时将对于的Cell背景色设置为红色。
- 当EnableRowVirtualization为false时,cellContent一直为null,所以就不能达到效果;
- 当EnableRowVirtualization为true时,运行后会发现当前已显示的行没有效果,但是当我们滚动行时,加载未显示的行,这些行是可以实现效果的。
此事件使您能够在重复使用行之前对该行进行任何必要的更改。 通常使用LoadingRow事件来撤消在事件处理程序中进行的任何更改;若要在重新使用行之前撤消这些自定义项,请处理 UnloadingRow 事件。
DataGridRow_MouseLeftButtonDown、DataGridRow_MouseDoubleClick
和行相关的鼠标事件需要在RowStyle中定义:
<DataGrid.RowStyle>
<Style TargetType="DataGridRow" BasedOn="{StaticResource gridRowStyle}">
<EventSetter Event="MouseDoubleClick" Handler="DataGridRow_MouseDoubleClick"/>
<EventSetter Event="MouseLeftButtonDown" Handler="DataGridRow_MouseLeftButtonDown"/>
</Style>
</DataGrid.RowStyle>
private void DataGridRow_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
DataGridRow row = sender as DataGridRow;
}
PS:行的Selected和UnSelected事件和触发器也是在RowStyle中定义,需要注意的是在该样式中设置选中行的背景色是无效,需要单独设置每个单元格的样式才有效。
DataGridCell_MouseLeftButtonDown、DataGridCell_MouseDoubleClick
和单元各相关的鼠标事件需要在CellStyle中定义:
<DataGrid.CellStyle>
<Style TargetType="DataGridCell" BasedOn="{StaticResource gridCellStyle}">
<EventSetter Event="MouseDoubleClick" Handler="DataGridCell_MouseDoubleClick"/>
<EventSetter Event="MouseLeftButtonDown" Handler="DataGridCell_MouseLeftButtonDown"/>
</Style>
</DataGrid.CellStyle>
private void DataGridCell_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
DataGridCell dataGridCell = sender as DataGridCell;
dataGridCell.Background = new SolidColorBrush(Colors.Gold);
}
PS:单元格的Selected和UnSelected事件和触发器也是在CellStyle中定义。
<Style TargetType="DataGridCell" x:Key="gridCellStyle">
<Setter Property="VerticalAlignment" Value="Stretch" />
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="#FFDEAD" />
<Setter Property="Foreground" Value="Black" />
<Setter Property="BorderThickness" Value="0" />
</Trigger>
</Style.Triggers>
</Style>
8.DataGrid数据获取
若是我们不是在DataGrid相关事件中如何获取DataGrid的每行,每列和每个单元格呢???
获取列
- ObservableCollection<DataGridColumn> columns = this.grid.Columns; 然后通过索引就可以获取对应的列
- DataGridColumn col0 = this.grid.ColumnFromDisplayIndex(0); 直接调用方法获取对于索引的列
获取行
通过ItemsControl的ItemContainerGenerator对象获取对应的行:
- DataGridRow row0 = this.grid.ItemContainerGenerator.ContainerFromIndex(0) as DataGridRow;
- DataGridRow row0 = this.grid.ItemContainerGenerator.ContainerFromItem(this.grid.Items[0]) as DataGridRow;
获取单元格
获取单元格需要先获取行和列:
- DataGridRow row0 = this.grid.ItemContainerGenerator.ContainerFromIndex(0) as DataGridRow;
DataGridColumn col0 = this.grid.ColumnFromDisplayIndex(0);
DataGridCell cell = col0.GetCellContent(row0).Parent as DataGridCell; - DataGridColumn col0 = this.grid.ColumnFromDisplayIndex(0);
DataGridCell cell = col0.GetCellContent(this.grid.Items[0]).Parent as DataGridCell;