【问题标题】:WPF DataGrid Dynamic width cell highlightingWPF DataGrid 动态宽度单元格突出显示
【发布时间】:2017-10-19 23:15:00
【问题描述】:

我有一个使用 WPF 数据网格的应用程序。该网格显示了一组测试结果。如果测试结果超出允许的最小值和最大值,我想用红色突出显示该单元格。我目前正在使用它,但对突出显示不太满意。

这是目前的样子:

这是你想要的样子(通过一些图片旋转):

请注意,第一个示例中的突出显示占用了整个单元格宽度。我希望有一个理想的例子,它只消耗与最宽结果一样多的空间,两边都有一点边距。请记住,任何一个单元格中的结果从一个样本到下一个样本的范围可能在 0 到 1920K 之间。这是一个边缘案例,但我希望突出显示的区域因此会增长和缩小。

仅供参考,这些结果会在可配置的计时器上更新,该计时器会根据用户配置在 10 毫秒到 10 秒之间触发。

以下是生成第一个示例的代码(抱歉,代码量很大)。有趣的部分是 DataGridCellStyle、ResultCellStyleCellTemplate

XAML

<Window x:Class="*_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:*_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 *_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));
        }
    }
}

【问题讨论】:

    标签: wpf templates datagrid styles datagridcell


    【解决方案1】:

    我认为这应该做你想要的:

    <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,它就起作用了。重要的部分并不难找到。

    【讨论】:

    • 太好了,我会试试这个并在星期一之前报告。
    • Ed,您的解决方案非常完美,感谢您的快速响应。