由於項目要顯示表頭合並,而數據源列隨時變更,又不想重復的畫表格,就實現動態數據(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模板數據的配置,實現數表格頭部的合並和數據顯示。
還有一種稍微復雜表格頭的合並,目前是列合並,可能存在行和列同時合並,已經有思路還未驗證是否可行,由於項目暫未用到不花費時間研究,園友有需要就在下方留言。
合並行的已經實現:效果如下

