WPF DataGridTable
由于项目要显示表头合并,而数据源列随时变更,又不想重复的画表格,就实现动态数据(dynamic)绑定和配置数据列模板的方式
编辑DataGridColumnHeader样式实现表头合并:效果如下
实现思路:
在表头中插入一个Grid,Grid列跟HeaderColmun列数相等,并关联HeaderColmun的SizeChanged事件,Colmun列大小发生变化时,合并的头模板也会跟着移动。
<Grid x:Name="PART_ColumnHeadersPresenter_Grid" Height="30" ShowGridLines="False"> </Grid>
Dictionary<DataGridColumnHeader, ColumnDefinition> dictCols = new Dictionary<DataGridColumnHeader, ColumnDefinition>(); private void DataGridTitleSpan_Loaded(object sender, RoutedEventArgs e) { var hdCols = WPFVisualTreeHelper.GetChildByName<DataGridColumnHeadersPresenter>(this, "PART_ColumnHeadersPresenter"); var grid = WPFVisualTreeHelper.GetChildByName<Grid>(this, "PART_ColumnHeadersPresenter_Grid"); if (grid == null) { return; } //grid.Visibility = Visibility.Collapsed; //if (DataSouceGridHeaderColTemplate == null || DataSouceGridHeaderColTemplate.Count == 0) //{ // grid.Visibility = Visibility.Collapsed; //} var hdItem = WPFVisualTreeHelper.FindVisualChild<DataGridCellsPanel>(hdCols); var header = hdItem.FirstOrDefault(); if (header != null) { foreach (var item in header.Children) { var vHd = item as DataGridColumnHeader; vHd.SizeChanged += VHd_SizeChanged; ColumnDefinition rd = new ColumnDefinition(); rd.Width = new GridLength(vHd.ActualWidth, GridUnitType.Pixel); grid.ColumnDefinitions.Add(rd); dictCols[vHd] = rd; } } GenerateHeader(DataSouceGridHeaderColTemplate, grid); } private void VHd_SizeChanged(object sender, SizeChangedEventArgs e) { var vHd = sender as DataGridColumnHeader; if (dictCols.ContainsKey(vHd)) { dictCols[vHd].Width = new GridLength(vHd.ActualWidth, GridUnitType.Pixel); } }
下面是完整的HeaderColmun列模板
<Style x:Key="DataGridColumnHeaderStyle_Colspan" TargetType="{x:Type DataGridColumnHeader}"> <Setter Property="VerticalContentAlignment" Value="Center" /> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type DataGridColumnHeader}"> <!--<Grid> <Themes:DataGridHeaderBorder BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" IsClickable="{TemplateBinding CanUserSort}" IsPressed="{TemplateBinding IsPressed}" IsHovered="{TemplateBinding IsMouseOver}" Padding="{TemplateBinding Padding}" SortDirection="{TemplateBinding SortDirection}" SeparatorBrush="{TemplateBinding SeparatorBrush}" SeparatorVisibility="{TemplateBinding SeparatorVisibility}"> <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" /> </Themes:DataGridHeaderBorder> <Thumb x:Name="PART_LeftHeaderGripper" HorizontalAlignment="Left" Style="{StaticResource ColumnHeaderGripperStyle}" /> <Thumb x:Name="PART_RightHeaderGripper" HorizontalAlignment="Right" Style="{StaticResource ColumnHeaderGripperStyle}" /> </Grid>--> <Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto"></RowDefinition> <RowDefinition Height="*"></RowDefinition> </Grid.RowDefinitions> <Border BorderBrush="{Binding BorderBrush,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type DataGrid}}}" Padding="{TemplateBinding Padding}" BorderThickness="0 0 0 1" Grid.Row="0" Visibility="{Binding Path=ColspanVisibility,RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}"> <Grid x:Name="PART_ColumnHeadersPresenter_Grid" Height="30" ShowGridLines="False"> </Grid> </Border> <Grid Grid.Row="1"> <Themes:DataGridHeaderBorder BorderBrush="{Binding BorderBrush,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type DataGrid}}}" BorderThickness="0 0 1 1" Background="Transparent" IsClickable="{TemplateBinding CanUserSort}" IsPressed="{TemplateBinding IsPressed}" IsHovered="{TemplateBinding IsMouseOver}" Padding="{TemplateBinding Padding}" SortDirection="{TemplateBinding SortDirection}" SeparatorBrush="{TemplateBinding SeparatorBrush}" SeparatorVisibility="{TemplateBinding SeparatorVisibility}"> <ContentPresenter HorizontalAlignment="{Binding HorizontalContentAlignment,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type DataGrid}}}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" /> </Themes:DataGridHeaderBorder> <Thumb x:Name="PART_LeftHeaderGripper" HorizontalAlignment="Left" Style="{StaticResource ColumnHeaderGripperStyle}" /> <Thumb x:Name="PART_RightHeaderGripper" HorizontalAlignment="Right" Style="{StaticResource ColumnHeaderGripperStyle}" /> </Grid> </Grid> </ControlTemplate> </Setter.Value> </Setter> </Style>
关键代码如下:
显示的View Xaml和Code
<UserControl x:Class="YunTong46View.Usr_TestDataGridTableView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:YunTong46View" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300"> <UserControl.Resources> <ResourceDictionary Source="Themes/DataGridStyle.xaml"> </ResourceDictionary> </UserControl.Resources> <StackPanel> <StackPanel Margin="20 15 20 10"> <TextBlock Text="出现滚动条:数据绑定" Foreground="Red" FontSize="20"></TextBlock> <local:DataGridTitleSpan x:Name="dataGridTitle2" Width="500" Height="200" VerticalAlignment="Top" HorizontalAlignment="Left" ScrollViewer.HorizontalScrollBarVisibility="Visible" ScrollViewer.VerticalScrollBarVisibility="Visible" DataSouceGridHeaderColTemplate="{Binding Headers0}" ItemsSource="{Binding DataSource}"> </local:DataGridTitleSpan> </StackPanel> <StackPanel Margin="20 10"> <TextBlock Text="dynamic动态类数据绑定不支持排序" Foreground="Red" FontSize="20"></TextBlock> <local:DataGridTitleSpan x:Name="dataGridTitle" Height="200" ScrollViewer.HorizontalScrollBarVisibility="Visible" ScrollViewer.VerticalScrollBarVisibility="Visible" DataSouceGridHeaderColTemplate="{Binding Headers0}"> </local:DataGridTitleSpan> </StackPanel> <StackPanel Margin="20 10"> <TextBlock Text="引用样式" Foreground="Red" FontSize="20"></TextBlock> <DataGrid x:Name="dataGrid" Height="200" Style="{DynamicResource DataGridStyle_Colspan}" ItemsSource="{Binding DataSource}" Visibility="Visible"> </DataGrid> </StackPanel> <StackPanel Margin="20 10"> <TextBlock Text="原生DataGrid" Foreground="Red" FontSize="20"></TextBlock> <DataGrid x:Name="dataGrid2" Height="200" BorderBrush="Blue" AutoGenerateColumns="False" HorizontalGridLinesBrush="Blue" VerticalGridLinesBrush="Blue" ItemsSource="{Binding DataSource}"> </DataGrid> </StackPanel> </StackPanel> </UserControl>
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.Navigation; using System.Windows.Shapes; namespace YunTong46View { /// <summary> /// Usr_YunTong46MainView.xaml 的交互逻辑 /// </summary> public partial class Usr_TestDataGridTableView : UserControl { ViewMode viewMode = new ViewMode(); public Usr_TestDataGridTableView() { InitializeComponent(); viewMode.Init(); this.DataContext = viewMode; SetHeaderTemplates(dataGridTitle, viewMode.Headers1); SetHeaderTemplates(dataGridTitle2, viewMode.Headers1); SetHeaderTemplates(dataGrid, viewMode.Headers1); SetHeaderTemplates(dataGrid2, viewMode.Headers1); this.Loaded += Usr_YunTong46MainView_Loaded; } private void Usr_YunTong46MainView_Loaded(object sender, RoutedEventArgs e) { var json = JsonHelper.SerializeObject(viewMode.DataSource); var dyDatas = JsonHelper.DeserializeObject<dynamic>(json); dataGridTitle.ItemsSource = dyDatas; } private void SetHeaderTemplates(DataGrid dataGrid, List<HeaderTemplate> headers) { foreach (var item in headers) { var col = new DataGridTextColumn(); col.Header = item.HeaderName; var bind = new Binding(); bind.Path = new PropertyPath(item.PropertyName); if (!string.IsNullOrEmpty(item.PropertyFormat)) { //bind.StringFormat = "{}{0:" + item.PropertyFormat + "}}"; bind.StringFormat = item.PropertyFormat.Trim(); } col.Binding = bind; DataGridLengthUnitType unType = DataGridLengthUnitType.Auto; double width = 0; if (item.ColmunUnitType == "p") { unType = DataGridLengthUnitType.Pixel; width = item.ColmunWidth; } else if (item.ColmunUnitType == "s") { unType = DataGridLengthUnitType.Star; width = item.ColmunWidth; } if (width < 0) { width = 0; } col.Width = new DataGridLength(width, unType); dataGrid.Columns.Add(col); } } } public class ViewMode { private List<HeaderTemplate> _headers0 = new List<HeaderTemplate>(); private List<HeaderTemplate> _headers1 = new List<HeaderTemplate>(); private List<DataModel> _dataSource = new List<DataModel>(); public List<HeaderTemplate> Headers0 { get { return _headers0; } set { _headers0 = value; } } public List<HeaderTemplate> Headers1 { get { return _headers1; } set { _headers1 = value; } } public List<DataModel> DataSource { get { return _dataSource; } set { _dataSource = value; } } public void Init() { _headers0.Add(new HeaderTemplate() { ColmunIndex = 0, ColmunSpan = 4, HeaderName = "故障登记时间及状态" }); _headers0.Add(new HeaderTemplate() { ColmunIndex = 0, ColmunSpan = 4, HeaderName = "通知时间及通知方法" }); _headers0.Add(new HeaderTemplate() { ColmunIndex = 0, ColmunSpan = 3, HeaderName = "到达时间及签名" }); _headers0.Add(new HeaderTemplate() { ColmunIndex = 0, ColmunSpan = 3, HeaderName = "消除不良及破损后的时间和方法" }); _headers1.Add(new HeaderTemplate() { ColmunIndex = 0, ColmunSpan = 0, PropertyName = "GZDT", PropertyFormat = "MM-dd", ColmunUnitType = "a", ColmunWidth = 1, HeaderName = "月日" }); _headers1.Add(new HeaderTemplate() { ColmunIndex = 1, ColmunSpan = 0, PropertyName = "GZDT", PropertyFormat = "HH:mm", ColmunUnitType = "a", ColmunWidth = 1, HeaderName = "时分" }); _headers1.Add(new HeaderTemplate() { ColmunIndex = 2, ColmunSpan = 0, PropertyName = "GZDeviceName", ColmunUnitType = "a", ColmunWidth = 1, HeaderName = "设备名称" }); _headers1.Add(new HeaderTemplate() { ColmunIndex = 3, ColmunSpan = 0, PropertyName = "GZStatus", ColmunUnitType = "a", ColmunWidth = 1, HeaderName = "故障状态" }); _headers1.Add(new HeaderTemplate() { ColmunIndex = 4, ColmunSpan = 0, PropertyName = "TZDW", ColmunUnitType = "a", ColmunWidth = 1, HeaderName = "通知单位" }); _headers1.Add(new HeaderTemplate() { ColmunIndex = 5, ColmunSpan = 0, PropertyName = "TZDT", PropertyFormat = "MM-dd", ColmunUnitType = "a", ColmunWidth = 1, HeaderName = "月日" }); _headers1.Add(new HeaderTemplate() { ColmunIndex = 6, ColmunSpan = 0, PropertyName = "TZDT", PropertyFormat = "HH:mm", ColmunUnitType = "a", ColmunWidth = 1, HeaderName = "时分" }); _headers1.Add(new HeaderTemplate() { ColmunIndex = 7, ColmunSpan = 0, PropertyName = "TZFF", ColmunUnitType = "a", ColmunWidth = 1, HeaderName = "通知方法" }); _headers1.Add(new HeaderTemplate() { ColmunIndex = 8, ColmunSpan = 0, PropertyName = "DDDT", PropertyFormat = "MM-dd", ColmunUnitType = "a", ColmunWidth = 1, HeaderName = "月日" }); _headers1.Add(new HeaderTemplate() { ColmunIndex = 9, ColmunSpan = 0, PropertyName = "DDDT", PropertyFormat = "HH:mm", ColmunUnitType = "a", ColmunWidth = 1, HeaderName = "时分" }); _headers1.Add(new HeaderTemplate() { ColmunIndex = 10, ColmunSpan = 0, PropertyName = "DDQM", ColmunUnitType = "a", ColmunWidth = 1, HeaderName = "该段工作人员到达后签名" }); _headers1.Add(new HeaderTemplate() { ColmunIndex = 11, ColmunSpan = 0, PropertyName = "BLDT", PropertyFormat = "MM-dd", ColmunUnitType = "a", ColmunWidth = 1, HeaderName = "月日" }); _headers1.Add(new HeaderTemplate() { ColmunIndex = 12, ColmunSpan = 0, PropertyName = "BLDT", PropertyFormat = "HH:mm", ColmunUnitType = "a", ColmunWidth = 1, HeaderName = "时分" }); _headers1.Add(new HeaderTemplate() { ColmunIndex = 13, ColmunSpan = 0, PropertyName = "BLTEXT", ColmunUnitType = "s", ColmunWidth = 10, HeaderName = "破损及不良的原因,采用何种方法进行\r;修理。工作人员及车站值班员签字" }); for (int i = 0; i < 10; i++) { DataModel model = new DataModel(); model.DDDT = DateTime.Now.AddMinutes(-1); model.GZDT = model.DDDT; model.TZDT = model.DDDT; model.BLDT = model.DDDT; model.GZDeviceName = "故障设备名称" + i.ToString(); model.GZStatus = "故障状态" + i.ToString(); model.TZDW = "通知单位" + i.ToString(); model.TZFF = "通知方法" + i.ToString(); model.DDQM = "到达签名" + i.ToString(); model.BLTEXT = "不良内容" + i.ToString(); _dataSource.Add(model); } } } public class DataModel { public DateTime GZDT { set; get; } public string GZDeviceName { set; get; } public string GZStatus { set; get; } public DateTime TZDT { set; get; } public string TZDW { set; get; } public string TZFF { set; get; } /// <summary> /// 到达时间 /// </summary> public DateTime DDDT { set; get; } public string DDQM { set; get; } public DateTime BLDT { set; get; } public string BLTEXT { set; get; } } }
DataTable样式和Code
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:Themes="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero"> <Style x:Key="DataGridRowStyle_ColSpan" TargetType="{x:Type DataGridRow}"> <Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.WindowBrushKey}}" /> <Setter Property="SnapsToDevicePixels" Value="true" /> <Setter Property="Validation.ErrorTemplate" Value="{x:Null}" /> <Setter Property="ValidationErrorTemplate"> <Setter.Value> <ControlTemplate> <TextBlock Foreground="Red" Margin="2,0,0,0" Text="!" VerticalAlignment="Center" /> </ControlTemplate> </Setter.Value> </Setter> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type DataGridRow}"> <Border x:Name="DGR_Border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="True"> <SelectiveScrollingGrid> <SelectiveScrollingGrid.ColumnDefinitions> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="*" /> </SelectiveScrollingGrid.ColumnDefinitions> <SelectiveScrollingGrid.RowDefinitions> <RowDefinition Height="*" /> <RowDefinition Height="Auto" /> </SelectiveScrollingGrid.RowDefinitions> <Border Grid.Column="1" BorderThickness="1 0 0 0" BorderBrush="{Binding BorderBrush,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type DataGrid}}}"> <DataGridCellsPresenter ItemsPanel="{TemplateBinding ItemsPanel}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" /> </Border> <DataGridDetailsPresenter Grid.Column="1" Grid.Row="1" SelectiveScrollingGrid.SelectiveScrollingOrientation="{Binding AreRowDetailsFrozen, ConverterParameter={x:Static SelectiveScrollingOrientation.Vertical}, Converter={x:Static DataGrid.RowDetailsScrollingConverter}, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}" Visibility="{TemplateBinding DetailsVisibility}" /> <!--隐藏左侧行号--> <DataGridRowHeader Grid.RowSpan="2" SelectiveScrollingGrid.SelectiveScrollingOrientation="Vertical" Visibility="Collapsed" /> </SelectiveScrollingGrid> </Border> </ControlTemplate> </Setter.Value> </Setter> <Style.Triggers> <Trigger Property="IsNewItem" Value="True"> <Setter Property="Margin" Value="{Binding NewItemMargin, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}" /> </Trigger> <Trigger Property="IsSelected" Value="True"> <Setter Property="Background" Value="#FFE8C91A" /> <Setter Property="Foreground" Value="Red" /> </Trigger> </Style.Triggers> </Style> <Style x:Key="DataGridCellStyle_Colspan" TargetType="{x:Type DataGridCell}"> <Setter Property="FontSize" Value="16" /> <Setter Property="HorizontalContentAlignment" Value="Center"></Setter> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type DataGridCell}"> <!--<ContentPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="Center" HorizontalAlignment="Center" />--> <ContentPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" /> </ControlTemplate> </Setter.Value> </Setter> <!--<Style.Triggers> <Trigger Property="IsSelected" Value="True"> <Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}" /> <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.HighlightTextBrushKey}}" /> <Setter Property="BorderBrush" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}" /> </Trigger> <Trigger Property="IsMouseOver" Value="True"> <Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}" /> </Trigger> <Trigger Property="IsKeyboardFocusWithin" Value="True"> <Setter Property="BorderBrush" Value="{DynamicResource {x:Static DataGrid.FocusBorderBrushKey}}" /> </Trigger> <Trigger Property="IsSelected" Value="True"> <Setter Property="Foreground" Value="Black" /> </Trigger> </Style.Triggers>--> </Style> <BooleanToVisibilityConverter x:Key="bool2VisibilityConverter" /> <!--<Style x:Key="RowHeaderGripperStyle" TargetType="{x:Type Thumb}"> <Setter Property="Height" Value="8" /> <Setter Property="Background" Value="Transparent" /> <Setter Property="Cursor" Value="SizeNS" /> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type Thumb}"> <Border Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}" /> </ControlTemplate> </Setter.Value> </Setter> </Style>--> <Style x:Key="DataGridRowHeaderStyle_Colspan" TargetType="{x:Type DataGridRowHeader}"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type DataGridRowHeader}"> <Grid> <Themes:DataGridHeaderBorder BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" IsPressed="{TemplateBinding IsPressed}" IsHovered="{TemplateBinding IsMouseOver}" IsSelected="{TemplateBinding IsRowSelected}" Orientation="Horizontal" Padding="{TemplateBinding Padding}" SeparatorBrush="{TemplateBinding SeparatorBrush}" SeparatorVisibility="{TemplateBinding SeparatorVisibility}"> <StackPanel Orientation="Horizontal"> <ContentPresenter RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="Center" /> <Control SnapsToDevicePixels="false" Template="{Binding ValidationErrorTemplate, RelativeSource={RelativeSource AncestorType={x:Type DataGridRow}}}" Visibility="{Binding (Validation.HasError), Converter={StaticResource bool2VisibilityConverter}, RelativeSource={RelativeSource AncestorType={x:Type DataGridRow}}}" /> </StackPanel> </Themes:DataGridHeaderBorder> <!--<Thumb x:Name="PART_TopHeaderGripper" Style="{StaticResource RowHeaderGripperStyle}" VerticalAlignment="Top" /> <Thumb x:Name="PART_BottomHeaderGripper" Style="{StaticResource RowHeaderGripperStyle}" VerticalAlignment="Bottom" />--> </Grid> </ControlTemplate> </Setter.Value> </Setter> </Style> <Style x:Key="ColumnHeaderGripperStyle" TargetType="{x:Type Thumb}"> <Setter Property="Width" Value="8" /> <Setter Property="Background" Value="Transparent" /> <Setter Property="Cursor" Value="SizeWE" /> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type Thumb}"> <Border Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}" /> </ControlTemplate> </Setter.Value> </Setter> </Style> <Style x:Key="DataGridColumnHeaderStyle_Colspan" TargetType="{x:Type DataGridColumnHeader}"> <Setter Property="VerticalContentAlignment" Value="Center" /> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type DataGridColumnHeader}"> <!--<Grid> <Themes:DataGridHeaderBorder BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" IsClickable="{TemplateBinding CanUserSort}" IsPressed="{TemplateBinding IsPressed}" IsHovered="{TemplateBinding IsMouseOver}" Padding="{TemplateBinding Padding}" SortDirection="{TemplateBinding SortDirection}" SeparatorBrush="{TemplateBinding SeparatorBrush}" SeparatorVisibility="{TemplateBinding SeparatorVisibility}"> <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" /> </Themes:DataGridHeaderBorder> <Thumb x:Name="PART_LeftHeaderGripper" HorizontalAlignment="Left" Style="{StaticResource ColumnHeaderGripperStyle}" /> <Thumb x:Name="PART_RightHeaderGripper" HorizontalAlignment="Right" Style="{StaticResource ColumnHeaderGripperStyle}" /> </Grid>--> <Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto"></RowDefinition> <RowDefinition Height="*"></RowDefinition> </Grid.RowDefinitions> <Border BorderBrush="{Binding BorderBrush,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type DataGrid}}}" Padding="{TemplateBinding Padding}" BorderThickness="0 0 0 1" Grid.Row="0" Visibility="{Binding Path=ColspanVisibility,RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}"> <Grid x:Name="PART_ColumnHeadersPresenter_Grid" Height="30" ShowGridLines="False"> </Grid> </Border> <Grid Grid.Row="1"> <Themes:DataGridHeaderBorder BorderBrush="{Binding BorderBrush,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type DataGrid}}}" BorderThickness="0 0 1 1" Background="Transparent" IsClickable="{TemplateBinding CanUserSort}" IsPressed="{TemplateBinding IsPressed}" IsHovered="{TemplateBinding IsMouseOver}" Padding="{TemplateBinding Padding}" SortDirection="{TemplateBinding SortDirection}" SeparatorBrush="{TemplateBinding SeparatorBrush}" SeparatorVisibility="{TemplateBinding SeparatorVisibility}"> <ContentPresenter HorizontalAlignment="{Binding HorizontalContentAlignment,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type DataGrid}}}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" /> </Themes:DataGridHeaderBorder> <Thumb x:Name="PART_LeftHeaderGripper" HorizontalAlignment="Left" Style="{StaticResource ColumnHeaderGripperStyle}" /> <Thumb x:Name="PART_RightHeaderGripper" HorizontalAlignment="Right" Style="{StaticResource ColumnHeaderGripperStyle}" /> </Grid> </Grid> </ControlTemplate> </Setter.Value> </Setter> </Style> <Style x:Key="DataGridStyle_Colspan" TargetType="{x:Type DataGrid}"> <Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" /> <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" /> <Setter Property="BorderBrush" Value="#FF688CAF" /> <Setter Property="HorizontalContentAlignment" Value="Center"></Setter> <Setter Property="BorderThickness" Value="0" /> <Setter Property="RowDetailsVisibilityMode" Value="VisibleWhenSelected" /> <Setter Property="ScrollViewer.CanContentScroll" Value="true" /> <Setter Property="ScrollViewer.PanningMode" Value="Both" /> <Setter Property="Stylus.IsFlicksEnabled" Value="False" /> <Setter Property="CanUserAddRows" Value="False" /> <Setter Property="BorderBrush" Value="Blue"> </Setter> <Setter Property="HorizontalGridLinesBrush" Value="Blue"> </Setter> <Setter Property="VerticalGridLinesBrush" Value="Blue"> </Setter> <Setter Property="AutoGenerateColumns" Value="False"> </Setter> <Setter Property="TextBlock.TextAlignment" Value="Center"> </Setter> <Setter Property="RowHeaderStyle" Value="{StaticResource DataGridRowHeaderStyle_Colspan}"> </Setter> <Setter Property="RowStyle" Value="{StaticResource DataGridRowStyle_ColSpan}"> </Setter> <Setter Property="ColumnHeaderStyle" Value="{StaticResource DataGridColumnHeaderStyle_Colspan}"> </Setter> <Setter Property="CellStyle" Value="{StaticResource DataGridCellStyle_Colspan}"> </Setter> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type DataGrid}"> <Border BorderBrush="{TemplateBinding BorderBrush}" Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}" SnapsToDevicePixels="True" x:Name="bd_Out"> <ScrollViewer x:Name="DG_ScrollViewer" Focusable="false"> <ScrollViewer.Template> <ControlTemplate TargetType="{x:Type ScrollViewer}"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="*" /> <ColumnDefinition Width="Auto" /> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition Height="*" /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <Button Command="{x:Static DataGrid.SelectAllCommand}" Focusable="false" Style="{DynamicResource {ComponentResourceKey ResourceId=DataGridSelectAllButtonStyle, TypeInTargetAssembly={x:Type DataGrid}}}" Visibility="{Binding HeadersVisibility, ConverterParameter={x:Static DataGridHeadersVisibility.All}, Converter={x:Static DataGrid.HeadersVisibilityConverter}, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}" Width="{Binding CellsPanelHorizontalOffset, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}" /> <Border BorderThickness="1 1 1 0" Margin="-1 0 0 0" BorderBrush="{Binding BorderBrush,ElementName=bd_Out}" Grid.Column="1"> <DataGridColumnHeadersPresenter x:Name="PART_ColumnHeadersPresenter" Visibility="{Binding HeadersVisibility, ConverterParameter={x:Static DataGridHeadersVisibility.Column}, Converter={x:Static DataGrid.HeadersVisibilityConverter}, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}" /> </Border> <ScrollContentPresenter x:Name="PART_ScrollContentPresenter" CanContentScroll="{TemplateBinding CanContentScroll}" Grid.ColumnSpan="2" Grid.Row="1" /> <ScrollBar x:Name="PART_VerticalScrollBar" Grid.Column="2" Maximum="{TemplateBinding ScrollableHeight}" Orientation="Vertical" Grid.Row="1" Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}" Value="{Binding VerticalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" ViewportSize="{TemplateBinding ViewportHeight}" /> <Grid Grid.Column="1" Grid.Row="2"> <Grid.ColumnDefinitions> <ColumnDefinition Width="{Binding NonFrozenColumnsViewportHorizontalOffset, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}" /> <ColumnDefinition Width="*" /> </Grid.ColumnDefinitions> <ScrollBar x:Name="PART_HorizontalScrollBar" Grid.Column="1" Maximum="{TemplateBinding ScrollableWidth}" Orientation="Horizontal" Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}" Value="{Binding HorizontalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" ViewportSize="{TemplateBinding ViewportWidth}" /> </Grid> </Grid> </ControlTemplate> </ScrollViewer.Template> <ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" /> </ScrollViewer> </Border> </ControlTemplate> </Setter.Value> </Setter> <Style.Triggers> <MultiTrigger> <MultiTrigger.Conditions> <Condition Property="IsGrouping" Value="true" /> <Condition Property="VirtualizingPanel.IsVirtualizingWhenGrouping" Value="false" /> </MultiTrigger.Conditions> <Setter Property="ScrollViewer.CanContentScroll" Value="false" /> </MultiTrigger> </Style.Triggers> </Style> </ResourceDictionary>
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.Controls.Primitives; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace YunTong46View { /// <summary> /// 按照步骤 1a 或 1b 操作,然后执行步骤 2 以在 XAML 文件中使用此自定义控件。 /// /// 步骤 1a) 在当前项目中存在的 XAML 文件中使用该自定义控件。 /// 将此 XmlNamespace 特性添加到要使用该特性的标记文件的根 /// 元素中: /// /// xmlns:MyNamespace="clr-namespace:YunTong46View" /// /// /// 步骤 1b) 在其他项目中存在的 XAML 文件中使用该自定义控件。 /// 将此 XmlNamespace 特性添加到要使用该特性的标记文件的根 /// 元素中: /// /// xmlns:MyNamespace="clr-namespace:YunTong46View;assembly=YunTong46View" /// /// 您还需要添加一个从 XAML 文件所在的项目到此项目的项目引用, /// 并重新生成以避免编译错误: /// /// 在解决方案资源管理器中右击目标项目,然后依次单击 /// “添加引用”->“项目”->[浏览查找并选择此项目] /// /// /// 步骤 2) /// 继续操作并在 XAML 文件中使用控件。 /// /// <MyNamespace:DataGridTitleSpan/> /// /// </summary> public class DataGridTitleSpan : DataGrid { ResourceDictionary rs = new ResourceDictionary(); static DataGridTitleSpan() { DefaultStyleKeyProperty.OverrideMetadata(typeof(DataGridTitleSpan), new FrameworkPropertyMetadata(typeof(DataGridTitleSpan))); } public DataGridTitleSpan() : base() { rs.Source = new Uri("/YunTong46View;component/Themes/DataGridStyle.xaml", UriKind.Relative); //RowHeaderStyle = rs["DataGridRowHeaderStyle_Colspan"] as Style; //RowStyle = rs["DataGridRowStyle_ColSpan"] as Style; //ColumnHeaderStyle = rs["DataGridColumnHeaderStyle_Colspan"] as Style; var style = rs["DataGridStyle_Colspan"] as Style; this.Style = style; this.Loaded += DataGridTitleSpan_Loaded; } public List<HeaderTemplate> DataSouceGridHeaderColTemplate { get { return (List<HeaderTemplate>)GetValue(DataSouceGridHeaderColTemplateProperty); } set { SetValue(DataSouceGridHeaderColTemplateProperty, value); } } // Using a DependencyProperty as the backing store for DataSouceGridHeaderTemplate. This enables animation, styling, binding, etc... public static readonly DependencyProperty DataSouceGridHeaderColTemplateProperty = DependencyProperty.Register("DataSouceGridHeaderColTemplate", typeof(List<HeaderTemplate>), typeof(DataGridTitleSpan), new PropertyMetadata(null)); public Visibility ColspanVisibility { get { return (Visibility)GetValue(ColspanVisibilityProperty); } set { SetValue(ColspanVisibilityProperty, value); } } // Using a DependencyProperty as the backing store for ColspanVisibility. This enables animation, styling, binding, etc... public static readonly DependencyProperty ColspanVisibilityProperty = DependencyProperty.Register("ColspanVisibility", typeof(Visibility), typeof(DataGridTitleSpan), new PropertyMetadata(Visibility.Visible)); Dictionary<DataGridColumnHeader, ColumnDefinition> dictCols = new Dictionary<DataGridColumnHeader, ColumnDefinition>(); private void DataGridTitleSpan_Loaded(object sender, RoutedEventArgs e) { var hdCols = WPFVisualTreeHelper.GetChildByName<DataGridColumnHeadersPresenter>(this, "PART_ColumnHeadersPresenter"); var grid = WPFVisualTreeHelper.GetChildByName<Grid>(this, "PART_ColumnHeadersPresenter_Grid"); if (grid == null) { return; } if (DataSouceGridHeaderColTemplate == null || DataSouceGridHeaderColTemplate.Count == 0) { (grid.Parent as Border).Visibility = Visibility.Collapsed; //ColspanVisibility = Visibility.Collapsed; return; } var hdItem = WPFVisualTreeHelper.FindVisualChild<DataGridCellsPanel>(hdCols); var header = hdItem.FirstOrDefault(); if (header != null) { foreach (var item in header.Children) { var vHd = item as DataGridColumnHeader; vHd.SizeChanged += VHd_SizeChanged; ColumnDefinition rd = new ColumnDefinition(); rd.Width = new GridLength(vHd.ActualWidth, GridUnitType.Pixel); grid.ColumnDefinitions.Add(rd); dictCols[vHd] = rd; } } GenerateHeader(DataSouceGridHeaderColTemplate, grid); } private void VHd_SizeChanged(object sender, SizeChangedEventArgs e) { var vHd = sender as DataGridColumnHeader; if (dictCols.ContainsKey(vHd)) { dictCols[vHd].Width = new GridLength(vHd.ActualWidth, GridUnitType.Pixel); } } private void GenerateHeader(List<HeaderTemplate> headers, Grid g) { int colIndex = 0; for (int i = 0; i < headers.Count; i++) { var col = headers[i]; g.Children.Add(BigTitle(colIndex, col.ColmunSpan, col.HeaderName)); colIndex += (col.ColmunSpan + col.ColmunIndex); } } private UIElement BigTitle(int col, int colspan, string text) { var txb = new TextBlock(); Border bd = new Border(); bd.BorderThickness = new Thickness(0, 0, 1, 0); bd.BorderBrush = this.BorderBrush; //bd.Background = Brushes.Red; bd.Child = txb; txb.HorizontalAlignment = HorizontalAlignment.Center; txb.VerticalAlignment = VerticalAlignment.Center; txb.Text = text; Grid.SetColumn(bd, col); if (colspan > 0) { Grid.SetColumnSpan(bd, colspan); } return bd; } } }
如果想修改表格颜色请设置下面三个Brush
<Setter Property="BorderBrush" Value="Blue"> </Setter> <Setter Property="HorizontalGridLinesBrush" Value="Blue"> </Setter> <Setter Property="VerticalGridLinesBrush" Value="Blue"> </Setter>
此功能有两个小问题:
1.DataGridTitleSpan_Loaded捕获了DataGrid的列,所以不能在UserControl或者window的Load事件中SetHeaderTemplates,只能再构造函数中设置列。
2.原本以为通过判断合并列的数据如果为空,那么自动隐藏Grid,但是不知道为什么不生效,只能通过依赖属性才能隐藏合并的头
if (DataSouceGridHeaderColTemplate == null || DataSouceGridHeaderColTemplate.Count == 0) { (grid.Parent as Border).Visibility = Visibility.Collapsed; //ColspanVisibility = Visibility.Collapsed; return; }
该示例主要的目的通过HeaderTemplate模板数据的配置,实现数表格头部的合并和数据显示。
还有一种稍微复杂表格头的合并,目前是列合并,可能存在行和列同时合并,已经有思路还未验证是否可行,由于项目暂未用到不花费时间研究,园友有需要就在下方留言。
合并行的已经实现:效果如下