WPF DataGrid 动态宽度单元格突出显示
WPF DataGrid Dynamic width cell highlighting
我有一个使用 WPF 数据网格的应用程序。该网格显示了一组测试结果。如果测试结果超出最小和最大允许值,我想用红色突出显示该单元格。我目前可以使用它,但对突出显示不太满意。
这是目前的样子:
这是您想要的外观(通过一些图像处理):
请注意第一个示例中的突出显示占用了整个单元格宽度。我希望得到理想的例子,它只消耗与最宽结果一样多的 space,两边都有一点余量。请记住,从一个样本到下一个样本,任何一个单元格中的结果范围都可能在 0 到 1920K 之间。这是一个边缘案例,但我希望突出显示的区域因此增大和缩小。
仅供参考,这些结果在可配置的计时器上更新,该计时器根据用户配置在 10 毫秒到 10 秒之间的任意时间触发。
下面是生成第一个例子的代码(代码量大请见谅)。有趣的位是 DataGridCellStyle、ResultCellStyle 和 CellTemplate
XAML
<Window x:Class="Whosebug_HighlightCell.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:Whosebug_HighlightCell"
mc:Ignorable="d"
Loaded="Window_Loaded"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<Style TargetType="DataGridColumnHeader">
<Setter Property="Background" Value="Transparent" />
<Setter Property="Foreground" Value="White" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="HorizontalContentAlignment" Value="Center" />
</Style>
<ControlTemplate x:Key="CellTemplate" TargetType="{x:Type DataGridCell}">
<Border Background="{TemplateBinding Background}">
<ContentPresenter Margin="12,0,0,0" />
</Border>
</ControlTemplate>
<Style x:Key="DataGridCellStyle" TargetType="DataGridCell">
<Setter Property="Background" Value="#707070" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="HorizontalContentAlignment" Value="Left" />
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Foreground" Value="#CCCCCC" />
</Trigger>
</Style.Triggers>
</Style>
<Style x:Key="ResultCellStyle" TargetType="DataGridCell"
BasedOn="{StaticResource DataGridCellStyle}">
<Setter Property="Template" Value="{StaticResource CellTemplate}" />
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsResultOutOfBounds,
StringFormat={}{0:0.00}}"
Value="True">
<Setter Property="Background" Value="Red" />
<Setter Property="Foreground" Value="White" />
</DataTrigger>
</Style.Triggers>
</Style>
<Style TargetType="DataGrid" BasedOn="{x:Null}">
<Setter Property="RowBackground" Value="#707070" />
<Setter Property="AutoGenerateColumns" Value="False" />
<Setter Property="IsReadOnly" Value="True" />
<Setter Property="Background" Value="#666666" />
<Setter Property="GridLinesVisibility" Value="None" />
<Setter Property="BorderBrush" Value="Transparent" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="CanUserSortColumns" Value="False" />
<Setter Property="HeadersVisibility" Value="Column" />
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="Foreground" Value="#CCCCCC" />
<Setter Property="RowDetailsVisibilityMode" Value="Collapsed" />
<Setter Property="CellStyle" Value="{StaticResource DataGridCellStyle}" />
</Style>
<!-- A left justified DataGridTextColumn -->
<Style x:Key="ElementLeftJustified">
<Setter Property="TextBlock.HorizontalAlignment" Value="Left" />
<Setter Property="TextBlock.Margin" Value="15,0,5,0" />
</Style>
<!-- A right justified DataGridTextColumn -->
<Style x:Key="ElementRightJustified">
<Setter Property="TextBlock.HorizontalAlignment" Value="Right" />
<Setter Property="TextBlock.Margin" Value="0,0,5,0" />
</Style>
</Window.Resources>
<Grid Background="#FF666666">
<Border Margin="20" >
<DataGrid x:Name="_testSummaryGrid"
ItemsSource="{Binding TestResults}">
<DataGrid.Columns>
<DataGridTextColumn
MinWidth="75" Header="Test"
Binding="{Binding TestName}"
ElementStyle="{StaticResource ElementLeftJustified}" />
<DataGridTextColumn
MinWidth="75" Header="Min"
Binding="{Binding Min, StringFormat={}{0:0.00}}"
ElementStyle="{StaticResource ElementRightJustified}" />
<DataGridTextColumn
MinWidth="75" Header="Result"
Binding="{Binding Result, StringFormat={}{0:0.00}}"
ElementStyle="{StaticResource ElementRightJustified}"
CellStyle="{StaticResource ResultCellStyle}" />
<DataGridTextColumn
MinWidth="75" Header="Max"
Binding="{Binding Max, StringFormat={}{0:0.00}}"
ElementStyle="{StaticResource ElementRightJustified}" />
</DataGrid.Columns>
</DataGrid>
</Border>
</Grid>
</Window>
视图模型
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel;
using System.Collections.ObjectModel;
namespace Whosebug_HighlightCell
{
public class ResultsViewModel : ViewModelBase
{
public ResultsViewModel()
{
TestResults = new ObservableCollection<TestResult>
{
{ new TestResult { TestGroup = "Circle",TestName = "Radius",
Min = 100, Max = 153, Result = 150} },
{ new TestResult { TestGroup = "Circle", TestName = "Min Radius",
Min = 0, Max = 90, Result = 97.59 } },
// And so on ...
};
}
public ObservableCollection<TestResult> TestResults { get; set; }
}
public class TestResult : ViewModelBase
{
public string TestGroup { get; set; }
public string TestName { get; set; }
public double Result { get; set; }
public double Min { get; set; }
public double Max { get; set; }
public bool IsResultOutOfBounds { get { return !(Result >= Min && Result <= Max); } }
}
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void FirePropertyChanged(string property)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
}
}
}
我认为这应该可以满足您的要求:
<ControlTemplate x:Key="ResultCellTemplate" TargetType="{x:Type DataGridCell}">
<Border Background="{TemplateBinding Background}">
<Grid
Margin="12,0,0,0"
HorizontalAlignment="Right"
>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="Result" />
</Grid.ColumnDefinitions>
<Border Grid.Column="0" x:Name="ContentPresenterBorder">
<ContentPresenter
/>
</Border>
</Grid>
</Border>
<ControlTemplate.Triggers>
<!--
That stringformat you had will have been ignored because the target
type isn't string.
-->
<DataTrigger Binding="{Binding IsResultOutOfBounds}" Value="True">
<Setter TargetName="ContentPresenterBorder" Property="Background" Value="Red" />
<Setter TargetName="ContentPresenterBorder" Property="TextElement.Foreground" Value="White" />
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<Style x:Key="ResultCellStyle" TargetType="DataGridCell"
BasedOn="{StaticResource DataGridCellStyle}">
<Setter Property="Template" Value="{StaticResource ResultCellTemplate}" />
</Style>
...但不要忘记在 DataGrid
上设置 Grid.IsSharedSizeScope="True"
。这与 ColumnDefinition
上的 SharedSizeGroup="Result"
一起使用,以确保 DataGrid
中任何位置名为 "Result" 的任何网格列都将动态调整为相同的宽度。
<DataGrid
x:Name="_testSummaryGrid"
ItemsSource="{Binding TestResults}"
Grid.IsSharedSizeScope="True"
>
顺便说一下,这是一个很好的例子。最好将其缩减为只有两个 DataGrid 列,但我将其粘贴进去,按 F5 键,它起作用了。重要的部分并不难找到。
我有一个使用 WPF 数据网格的应用程序。该网格显示了一组测试结果。如果测试结果超出最小和最大允许值,我想用红色突出显示该单元格。我目前可以使用它,但对突出显示不太满意。
这是目前的样子:
这是您想要的外观(通过一些图像处理):
请注意第一个示例中的突出显示占用了整个单元格宽度。我希望得到理想的例子,它只消耗与最宽结果一样多的 space,两边都有一点余量。请记住,从一个样本到下一个样本,任何一个单元格中的结果范围都可能在 0 到 1920K 之间。这是一个边缘案例,但我希望突出显示的区域因此增大和缩小。
仅供参考,这些结果在可配置的计时器上更新,该计时器根据用户配置在 10 毫秒到 10 秒之间的任意时间触发。
下面是生成第一个例子的代码(代码量大请见谅)。有趣的位是 DataGridCellStyle、ResultCellStyle 和 CellTemplate
XAML
<Window x:Class="Whosebug_HighlightCell.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:Whosebug_HighlightCell"
mc:Ignorable="d"
Loaded="Window_Loaded"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<Style TargetType="DataGridColumnHeader">
<Setter Property="Background" Value="Transparent" />
<Setter Property="Foreground" Value="White" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="HorizontalContentAlignment" Value="Center" />
</Style>
<ControlTemplate x:Key="CellTemplate" TargetType="{x:Type DataGridCell}">
<Border Background="{TemplateBinding Background}">
<ContentPresenter Margin="12,0,0,0" />
</Border>
</ControlTemplate>
<Style x:Key="DataGridCellStyle" TargetType="DataGridCell">
<Setter Property="Background" Value="#707070" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="HorizontalContentAlignment" Value="Left" />
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Foreground" Value="#CCCCCC" />
</Trigger>
</Style.Triggers>
</Style>
<Style x:Key="ResultCellStyle" TargetType="DataGridCell"
BasedOn="{StaticResource DataGridCellStyle}">
<Setter Property="Template" Value="{StaticResource CellTemplate}" />
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsResultOutOfBounds,
StringFormat={}{0:0.00}}"
Value="True">
<Setter Property="Background" Value="Red" />
<Setter Property="Foreground" Value="White" />
</DataTrigger>
</Style.Triggers>
</Style>
<Style TargetType="DataGrid" BasedOn="{x:Null}">
<Setter Property="RowBackground" Value="#707070" />
<Setter Property="AutoGenerateColumns" Value="False" />
<Setter Property="IsReadOnly" Value="True" />
<Setter Property="Background" Value="#666666" />
<Setter Property="GridLinesVisibility" Value="None" />
<Setter Property="BorderBrush" Value="Transparent" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="CanUserSortColumns" Value="False" />
<Setter Property="HeadersVisibility" Value="Column" />
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="Foreground" Value="#CCCCCC" />
<Setter Property="RowDetailsVisibilityMode" Value="Collapsed" />
<Setter Property="CellStyle" Value="{StaticResource DataGridCellStyle}" />
</Style>
<!-- A left justified DataGridTextColumn -->
<Style x:Key="ElementLeftJustified">
<Setter Property="TextBlock.HorizontalAlignment" Value="Left" />
<Setter Property="TextBlock.Margin" Value="15,0,5,0" />
</Style>
<!-- A right justified DataGridTextColumn -->
<Style x:Key="ElementRightJustified">
<Setter Property="TextBlock.HorizontalAlignment" Value="Right" />
<Setter Property="TextBlock.Margin" Value="0,0,5,0" />
</Style>
</Window.Resources>
<Grid Background="#FF666666">
<Border Margin="20" >
<DataGrid x:Name="_testSummaryGrid"
ItemsSource="{Binding TestResults}">
<DataGrid.Columns>
<DataGridTextColumn
MinWidth="75" Header="Test"
Binding="{Binding TestName}"
ElementStyle="{StaticResource ElementLeftJustified}" />
<DataGridTextColumn
MinWidth="75" Header="Min"
Binding="{Binding Min, StringFormat={}{0:0.00}}"
ElementStyle="{StaticResource ElementRightJustified}" />
<DataGridTextColumn
MinWidth="75" Header="Result"
Binding="{Binding Result, StringFormat={}{0:0.00}}"
ElementStyle="{StaticResource ElementRightJustified}"
CellStyle="{StaticResource ResultCellStyle}" />
<DataGridTextColumn
MinWidth="75" Header="Max"
Binding="{Binding Max, StringFormat={}{0:0.00}}"
ElementStyle="{StaticResource ElementRightJustified}" />
</DataGrid.Columns>
</DataGrid>
</Border>
</Grid>
</Window>
视图模型
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel;
using System.Collections.ObjectModel;
namespace Whosebug_HighlightCell
{
public class ResultsViewModel : ViewModelBase
{
public ResultsViewModel()
{
TestResults = new ObservableCollection<TestResult>
{
{ new TestResult { TestGroup = "Circle",TestName = "Radius",
Min = 100, Max = 153, Result = 150} },
{ new TestResult { TestGroup = "Circle", TestName = "Min Radius",
Min = 0, Max = 90, Result = 97.59 } },
// And so on ...
};
}
public ObservableCollection<TestResult> TestResults { get; set; }
}
public class TestResult : ViewModelBase
{
public string TestGroup { get; set; }
public string TestName { get; set; }
public double Result { get; set; }
public double Min { get; set; }
public double Max { get; set; }
public bool IsResultOutOfBounds { get { return !(Result >= Min && Result <= Max); } }
}
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void FirePropertyChanged(string property)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
}
}
}
我认为这应该可以满足您的要求:
<ControlTemplate x:Key="ResultCellTemplate" TargetType="{x:Type DataGridCell}">
<Border Background="{TemplateBinding Background}">
<Grid
Margin="12,0,0,0"
HorizontalAlignment="Right"
>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="Result" />
</Grid.ColumnDefinitions>
<Border Grid.Column="0" x:Name="ContentPresenterBorder">
<ContentPresenter
/>
</Border>
</Grid>
</Border>
<ControlTemplate.Triggers>
<!--
That stringformat you had will have been ignored because the target
type isn't string.
-->
<DataTrigger Binding="{Binding IsResultOutOfBounds}" Value="True">
<Setter TargetName="ContentPresenterBorder" Property="Background" Value="Red" />
<Setter TargetName="ContentPresenterBorder" Property="TextElement.Foreground" Value="White" />
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<Style x:Key="ResultCellStyle" TargetType="DataGridCell"
BasedOn="{StaticResource DataGridCellStyle}">
<Setter Property="Template" Value="{StaticResource ResultCellTemplate}" />
</Style>
...但不要忘记在 DataGrid
上设置 Grid.IsSharedSizeScope="True"
。这与 ColumnDefinition
上的 SharedSizeGroup="Result"
一起使用,以确保 DataGrid
中任何位置名为 "Result" 的任何网格列都将动态调整为相同的宽度。
<DataGrid
x:Name="_testSummaryGrid"
ItemsSource="{Binding TestResults}"
Grid.IsSharedSizeScope="True"
>
顺便说一下,这是一个很好的例子。最好将其缩减为只有两个 DataGrid 列,但我将其粘贴进去,按 F5 键,它起作用了。重要的部分并不难找到。