DataGrid
每行背景色交替
<Style x:Key="DataGridRowStyleEx" TargetType="DataGridRow" BasedOn="{StaticResource DataGridRowBaseStyle}">
<Setter Property="MinHeight" Value="34" />
<Setter Property="FontSize" Value="14"/>
<Setter Property="Background" Value="Red" />
</Style>
<Style x:Key="DataGridCellStyleEx" TargetType="DataGridCell" BasedOn="{StaticResource DataGridCellBaseStyle}">
<Setter Property="Padding" Value="0" />
<Setter Property="Margin" Value="0" />
<Setter Property="FontSize" Value="14"/>
</Style>
<Style x:Key="DataGridColumnHeaderStyleEx" TargetType="DataGridColumnHeader" BasedOn="{StaticResource DataGridColumnHeaderBaseStyle}">
<Setter Property="BorderThickness" Value="0,0,0,2" />
<Setter Property="BorderBrush" Value="{StaticResource BorderBrush}"/>
<Setter Property="Height" Value="50"/>
<Setter Property="Padding" Value="0" />
<Setter Property="Background" Value="{StaticResource MainBackBrush}" />
</Style>
<Style x:Key="DataGridStyleEx" TargetType="DataGrid" BasedOn="{StaticResource DataGridBaseStyle}">
<Setter Property="RowStyle" Value="{StaticResource DataGridRowStyleEx}" />
<Setter Property="CellStyle" Value="{StaticResource DataGridCellStyleEx}" />
<Setter Property="ColumnHeaderStyle" Value="{StaticResource DataGridColumnHeaderStyleEx}" />
<Setter Property="ColumnWidth" Value="*" />
<Setter Property="RowHeight" Value="50" />
</Style>
<DataGrid Style="{StaticResource DataGridStyleEx}"
AlternationCount="2"
AlternatingRowBackground="Green"
LoadingRow="DataGrid_LoadingRow" CanUserSortColumns="False">
</DataGrid>
表头设置
行头自动增长,列头设置文本
mxl
<UserControl.Resources>
<cvt:ConvertItemToIndex x:Key="indexConverter"/>
<Style x:Key="SerialNumStyle" TargetType="TextBlock">
<Setter Property="Background" Value="WhiteSmoke" />
<Setter Property="Foreground" Value="Gray" />
<Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="FontSize" Value="{Binding FontSize,RelativeSource={RelativeSource AncestorType=DataGrid}}" />
</Style>
</UserControl.Resources>
<DataGrid x:Name="dgData" RowHeaderWidth="100" Loaded="DgData_Loaded" >
<DataGrid.Columns>
</DataGrid.Columns>
<DataGrid.RowHeaderTemplate>
<DataTemplate>
<TextBlock>
<TextBlock.Text>
<MultiBinding Converter="{StaticResource indexConverter}" ConverterParameter="">
<Binding RelativeSource="{RelativeSource AncestorType=DataGridRow}" />
<Binding Path="PageIndex" ElementName="pager" />
<Binding Path="PageSize" ElementName="pager" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</DataTemplate>
</DataGrid.RowHeaderTemplate>
</DataGrid>
<ctl:UC_Pager x:Name="pager" Grid.Row="2" GetDataDelegateHandler="GetData"/>
后台
private void DgData_Loaded(object sender, RoutedEventArgs e)
{
var dataGrid = (DataGrid)sender;
var border = (Border)VisualTreeHelper.GetChild(dataGrid, 0);
var scrollViewer = (ScrollViewer)VisualTreeHelper.GetChild(border, 0);
var grid = (Grid)VisualTreeHelper.GetChild(scrollViewer, 0);
var button = (Button)VisualTreeHelper.GetChild(grid, 0);
button.IsEnabled = false;
var textBlock = new TextBlock()
{
Text = "序号",
Style = (Style)this.FindResource("SerialNumStyle"),
};
grid.Children.Add(textBlock);
}
using System;
using System.Globalization;
using System.Windows.Controls;
using System.Windows.Data;
class ConvertItemToIndex : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
var row = values[0] as DataGridRow;
var pageIndex = System.Convert.ToInt32(values[1]);
var pageSize = System.Convert.ToInt32(values[2]);
return (row.GetIndex() + 1 + (pageIndex - 1) * pageSize).ToString();
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
行头隐藏,设置Header
<DataGrid x:Name="dgData" RowHeaderWidth="100" LoadingRow="DgData_LoadingRow">
<DataGrid.Columns>
<DataGridTemplateColumn Width="55" Header="序号" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type DataGridRow}}, Path=Header}" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="10,0,0,0"></TextBlock>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
<ctl:UC_Pager x:Name="pager" GetDataDelegateHandler="GetData"/>
private void DgData_LoadingRow(object sender, DataGridRowEventArgs e)
{
e.Row.Header = e.Row.GetIndex() + 1 + (pager.PageIndex - 1) * pager.PageSize;
}
行头可勾选
<DataGrid.Columns>
<DataGridTemplateColumn Width="50">
<DataGridTemplateColumn.HeaderTemplate>
<DataTemplate>
<CheckBox x:Name="cbAll" Click="CbAll_Click" Margin="3,0,0,0"/>
</DataTemplate>
</DataGridTemplateColumn.HeaderTemplate>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox Name="Select" HorizontalAlignment="Center" IsChecked="{Binding IsChecked}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="Guid" Binding="{Binding Guid}"/>
</DataGrid.Columns>
复制单行
DataGridTextColumn可正常Ctrl+C复制,不用响应CopyingRowClipboardContent。
DataGridTemplateColumn获取null,可对模板中的控件判断类型,转换获取,并注意虚拟化的影响。代码如下:
using System.Windows.Media;
private void DgData_CopyingRowClipboardContent(object sender, DataGridRowClipboardEventArgs e)
{
try
{
var dg = sender as DataGrid;
var result = new List<DataGridClipboardCellContent>();
// 第一行鼠标所在单元格值
var dataItem = dg.SelectedItem;
foreach (var item in e.ClipboardRowContent)
{
if (item.Content == null)
{
var element = dg.Columns[item.Column.DisplayIndex].GetCellContent(dataItem);
if (element == null)
{
result.Add(new DataGridClipboardCellContent(item, item.Column, $"<{item.Column.Header}>"));
// 因为虚拟化,超过可见范围的数据取不到值,可直接在数据源上查找
string value = string.Empty;
var entity = (Demo)dataItem;
switch (item.Column.DisplayIndex)
{
case 11:
value = entity.Property1;
break;
case 12:
value = entity.Property2;
break;
default:
break;
}
result.Add(new DataGridClipboardCellContent(item, item.Column, value));
continue;
}
if (element is TextBlock txt) // DataGridTextColumn
{
result.Add(new DataGridClipboardCellContent(item, item.Column, txt.Text));
}
else if (VisualTreeHelper.GetChild(element, 0) is TextBlock txt2) // DataGridTemplateColumn
{
result.Add(new DataGridClipboardCellContent(item, item.Column, txt2.Text));
}
}
else
{
result.Add(new DataGridClipboardCellContent(item, item.Column, item.Content.ToString()));
}
}
e.ClipboardRowContent.Clear();
e.ClipboardRowContent.AddRange(result);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
复制多行
private int _clipboardCalledCnt = 0;
private void DgData_CopyingRowClipboardContent(object sender, DataGridRowClipboardEventArgs e)
{
try
{
var dg = sender as DataGrid;
int rowCnt = dg.SelectedItems.Count;
var result = new List<DataGridClipboardCellContent>();
var dataItem = dg.SelectedItems[_clipboardCalledCnt];
foreach (var item in e.ClipboardRowContent)
{
if (item.Content == null)
{
var element = dg.Columns[item.Column.DisplayIndex].GetCellContent(dataItem);
if (element == null)
{
result.Add(new DataGridClipboardCellContent(item, item.Column, $"<{item.Column.Header}>"));
// 因为虚拟化,超过可见范围的数据取不到值,可直接在数据源上查找
string value = string.Empty;
var entity = (Demo)dataItem;
switch (item.Column.DisplayIndex)
{
case 11:
value = entity.Property1;
break;
case 12:
value = entity.Property2;
break;
default:
break;
}
result.Add(new DataGridClipboardCellContent(item, item.Column, value));
continue;
}
if (element is TextBlock txt) // DataGridTextColumn
{
result.Add(new DataGridClipboardCellContent(item, item.Column, txt.Text));
}
else if (VisualTreeHelper.GetChild(element, 0) is TextBlock txt2) // DataGridTemplateColumn
{
result.Add(new DataGridClipboardCellContent(item, item.Column, txt2.Text));
}
}
else
{
result.Add(new DataGridClipboardCellContent(item, item.Column, item.Content.ToString()));
}
}
e.ClipboardRowContent.Clear();
e.ClipboardRowContent.AddRange(result);
_clipboardCalledCnt++;
if (_clipboardCalledCnt == rowCnt)
{
_clipboardCalledCnt = 0;
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
属性绑定
DataGrid中,Foreground不是DataGridTextColumn的依赖项属性,无法实现属性绑定。
可以使用DataGridTemplateColumn+TextBlock代替实现。
控件行列扩展
单列提示框
<DataGridTemplateColumn Header="提示" Width="2*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=PropertyName}" ToolTip="{Binding Path=PropertyName}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
DataGridComboBoxColumn单击响应
<UserControl.Resources>
<cvt:PropertyTypeConv x:Key="propertyTypeConverter" />
</UserControl.Resources>
<ctl:SingleDataGridComboBoxColumn SelectedValueBinding="{Binding PropertyName1}" >
<ctl:SingleDataGridComboBoxColumn.ElementStyle>
<Style TargetType="ComboBox">
<Setter Property="SelectedValue" Value="{Binding Path=Key, Converter={StaticResource propertyTypeConverter}}"/>
</Style>
</ctl:SingleDataGridComboBoxColumn.ElementStyle>
<ctl:SingleDataGridComboBoxColumn.EditingElementStyle>
<Style TargetType="ComboBox">
<Setter Property="SelectedValue" Value="{Binding Path=Value}"/>
<EventSetter Event="SelectionChanged" Handler="ComboBox_SelectionChanged"/>
</Style>
</ctl:SingleDataGridComboBoxColumn.EditingElementStyle>
</ctl:SingleDataGridComboBoxColumn>
class SingleDataGridComboBoxColumn : DataGridComboBoxColumn
{
protected override object PrepareCellForEdit(FrameworkElement editingElement, RoutedEventArgs editingEventArgs)
{
object x = base.PrepareCellForEdit(editingElement, editingEventArgs);
if ((editingEventArgs is MouseButtonEventArgs) && ((MouseButtonEventArgs)editingEventArgs).ChangedButton == MouseButton.Left)
{
ComboBox o = editingElement as ComboBox;
if (o != null)
{
o.IsDropDownOpen = true;
}
}
return x;
}
}
虚化
UI虚拟化的原理是:但是由于显示器和人眼的限制,用户往往只会同时看到其中的数十条数据,因此只要在界面上渲染用户所看到的那些数据即可,对于用户呈现的界面仍然是一样的。
DataGrid
DataGrid默认开启虚化。渲染性能优化配置如下:
<DataGrid ItemsSource="{Binding DemoList, IsAsync=True}"
ScrollViewer.CanContentScroll="True" <!--False情况下,整个控件的虚拟化会取消-->
VirtualizingPanel.IsVirtualizing="True"
VirtualizingPanel.VirtualizationMode="Recycling"
VirtualizingPanel.IsContainerVirtualizable="True"
EnableColumnVirtualization="True"
EnableRowVirtualization="True">
<DataGrid.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</DataGrid.ItemsPanel>
<DataGrid.Columns>
</DataGrid.Columns>
</DataGrid>
ItemsControl
<ItemsControl ItemsSource="{Binding DemoList}" ItemTemplate="{StaticResource key1}"
VirtualizingPanel.IsVirtualizing="True" >
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<common:VirtualizingWrapPanel ScrollOffset="50" ChildHeight="90" ChildWidth="150"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.Template>
<ControlTemplate>
<ScrollViewer CanContentScroll="True"> <!--逻辑滚动-->
<ItemsPresenter/>
</ScrollViewer>
</ControlTemplate>
</ItemsControl.Template>
</ItemsControl>
关于UI虚化的实现,其核心则是VirtualPanel,在WPF中内置的VirtualPanel只有VirtualizingStackPanel一个,一般常用于表格之类的数据呈现。如果需要其它布局方式的Panel,比如VirtualizingWrapPanel则需要自己实现。