具有验证错误样式的 WPF Datagrid 单元格

发布于 2024-08-24 20:26:42 字数 95 浏览 7 评论 0原文

当出现验证错误时,我试图更改 DataGridCell(在 WPF 工具包 DataGrid 中)的默认样式。默认为红色边框。我怎样才能放置自己的模板?

谢谢。

I am trying to change the default style of a DataGridCell (within a WPF Toolkit DataGrid) when there is a validation error. The default is a red border. How can I put my own template?

Thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

早茶月光 2024-08-31 20:26:42

试试这个:

<!-- Cell Style -->
    <Style x:Key="CellErrorStyle" TargetType="{x:Type TextBlock}">
        <Style.Triggers>
            <Trigger Property="Validation.HasError" Value="true">
                <Setter Property="ToolTip"
                        Value="{Binding RelativeSource={RelativeSource Self},
                                Path=(Validation.Errors)[0].ErrorContent}"/>
                <Setter Property="Background" Value="Yellow"/>
            </Trigger>
        </Style.Triggers>
    </Style>

并使用它:

        <DataGrid.Columns>
            <DataGridTextColumn 
                ElementStyle="{StaticResource CellErrorStyle}">
            </DataGridTextColumn>
        </DataGrid.Columns>

Try this:

<!-- Cell Style -->
    <Style x:Key="CellErrorStyle" TargetType="{x:Type TextBlock}">
        <Style.Triggers>
            <Trigger Property="Validation.HasError" Value="true">
                <Setter Property="ToolTip"
                        Value="{Binding RelativeSource={RelativeSource Self},
                                Path=(Validation.Errors)[0].ErrorContent}"/>
                <Setter Property="Background" Value="Yellow"/>
            </Trigger>
        </Style.Triggers>
    </Style>

And use it:

        <DataGrid.Columns>
            <DataGridTextColumn 
                ElementStyle="{StaticResource CellErrorStyle}">
            </DataGridTextColumn>
        </DataGrid.Columns>
柏拉图鍀咏恒 2024-08-31 20:26:42

有一个不错的教程 来自 Diederik Krols 的内容完全符合您对 WPF Toolkit DataGrid 的要求。

There's a nice tutorial from Diederik Krols that does exactly what you're asking for the WPF Toolkit DataGrid.

时光清浅 2024-08-31 20:26:42

下面是一种解决方案,但首先,让我分享我的发现。

验证错误似乎永远不会到达列的 ElementStyle 或 CellStyle 内部。我怀疑这是因为它达到并可以在列的 EditingElementStyle 和数据网格的 RowStyle 中使用。

例如,您可以根据 Validation.HasError: 设置样式,

<DataGrid.RowStyle>
    <Style TargetType="{x:Type DataGridRow}">
        <Style.Triggers>
            <Trigger Property="Validation.HasError" Value="True">
                <Setter Property="Background" Value="Red" />
            </Trigger>
        </Style.Triggers>
    </Style>
</DataGrid.RowStyle>

或者也可以设置 Validation.ErrorTemplate: ,

<DataGrid.RowStyle>
    <Style TargetType="{x:Type DataGridRow}">
        <Setter Property="Validation.ErrorTemplate">
            <Setter.Value>
                <ControlTemplate>
                    <Border BorderBrush="Red" BorderThickness="3">
                        <AdornedElementPlaceholder />
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</DataGrid.RowStyle>

两者都可以正常工作。在 EditingElementStyle 上也是如此。这些都没有真正解决问题:更改行样式显然不会显示错误所在的单元格,并且一旦文本框散焦,编辑样式就不可见。

不幸的是,由于某种原因,相同的方法不适用于 ElementStyle 或 CellStyle。我倾向于相信这是一个错误,因为在 本教程在展示了在 EditingElementStyle 上设置 Validation.HasError 触发样式的示例后说道:

您可以通过替换来实现更广泛的自定义
列使用的 CellStyle。

解决方案

一种解决方法是不使用触发器,而是将单元格的背景(或任何您想要的样式属性)绑定到数据对象的新属性。我会表明我的意思。

在此示例中,有一些产品,它们有一个类别,该类别将显示在数据网格中的文本列中。这是 XAML:

<DataGrid AutoGenerateColumns="False" ItemsSource="{Binding Products}">
    <DataGrid.Columns>
        <!-- other columns -->
        <DataGridTextColumn Header="Category">
            <DataGridTextColumn.CellStyle>
                <Style TargetType="{x:Type DataGridCell}">
                    <Setter Property="Background"
                            Value="{Binding Mode=OneWay, Path=CategoryErrorBackgroundColor}" />
                </Style>
            </DataGridTextColumn.CellStyle>
            <DataGridTextColumn.Binding>
                <Binding Path="Category" UpdateSourceTrigger="PropertyChanged">
                    <Binding.ValidationRules>
                        <ExceptionValidationRule />
                    </Binding.ValidationRules>
                </Binding>
            </DataGridTextColumn.Binding>
        </DataGridTextColumn>
        <!-- other columns -->
    </DataGrid.Columns>
</DataGrid>

这是 Product 类:

public class Product : INotifyPropertyChanged
{
    // ...other fields and properties
    
    private string category;
    private SolidColorBrush categoryErrorBackgroundColor;

    public string Category
    {
        get
        {
            return category;
        }
        set
        {
            // validation checks
            if (value.Lenght < 5)
            {
                CategoryErrorBackgroundColor = Brushes.Red;
                // Notice that throwing is not even necessary for this solution to work
                throw new ArgumentException("Category cannot be shorter than 5 characters.");
            }
            else
            {
                CategoryErrorBackgroundColor = Brushes.Transparent;
            }
            category = value;
        }
    }
    // This is the property I'm binding to the cell's background
    // It has to have the appropriate type
    public SolidColorBrush CategoryErrorBackgroundColor
    {
        get
        {
            return categoryErrorBackgroundColor;
        }
        set
        {
            categoryErrorBackgroundColor = value;
            OnPropertyChanged();
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        var handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
}

显然,此解决方案的巨大缺点是它需要为数据对象中的每个属性提供一个(或多个,如果您想要更复杂的样式)样式属性,并且需要大量手动操作这些属性的设置。但这仍然是一种解决方案。

One kind of solution is below, but first, let me share my findings.

It seems like the validation errors never reach inside the column's ElementStyle or CellStyle. The reason I suspect this is because it reaches and can be used in the column's EditingElementStyle and the datagrid's RowStyle.

For example, you can set the style based on Validation.HasError:

<DataGrid.RowStyle>
    <Style TargetType="{x:Type DataGridRow}">
        <Style.Triggers>
            <Trigger Property="Validation.HasError" Value="True">
                <Setter Property="Background" Value="Red" />
            </Trigger>
        </Style.Triggers>
    </Style>
</DataGrid.RowStyle>

or you can set the Validation.ErrorTemplate as well:

<DataGrid.RowStyle>
    <Style TargetType="{x:Type DataGridRow}">
        <Setter Property="Validation.ErrorTemplate">
            <Setter.Value>
                <ControlTemplate>
                    <Border BorderBrush="Red" BorderThickness="3">
                        <AdornedElementPlaceholder />
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</DataGrid.RowStyle>

and both work just fine. Same on the EditingElementStyle. Neither of these really solves the problem: changing the row style obviously doesn't show which cell the error is in, and the editing style is not visible once the text box is defocused.

Unfortunately, for some reason, the same method doesn't work on the ElementStyle or the CellStyle. I'm inclined to believe this is a bug because in this tutorial it says after showing an example of setting a Validation.HasError triggered style on the EditingElementStyle:

You can implement more extensive customization by replacing the
CellStyle used by the column.

The solution

One workaround is not to use a trigger but rather bind the background (or whatever style property you want) of the cell to a new property of the data object. I'll show what I mean.

In this example, there are products, they have a category, which will be displayed in a text column in the datagrid. Here's the XAML:

<DataGrid AutoGenerateColumns="False" ItemsSource="{Binding Products}">
    <DataGrid.Columns>
        <!-- other columns -->
        <DataGridTextColumn Header="Category">
            <DataGridTextColumn.CellStyle>
                <Style TargetType="{x:Type DataGridCell}">
                    <Setter Property="Background"
                            Value="{Binding Mode=OneWay, Path=CategoryErrorBackgroundColor}" />
                </Style>
            </DataGridTextColumn.CellStyle>
            <DataGridTextColumn.Binding>
                <Binding Path="Category" UpdateSourceTrigger="PropertyChanged">
                    <Binding.ValidationRules>
                        <ExceptionValidationRule />
                    </Binding.ValidationRules>
                </Binding>
            </DataGridTextColumn.Binding>
        </DataGridTextColumn>
        <!-- other columns -->
    </DataGrid.Columns>
</DataGrid>

And here's the Product class:

public class Product : INotifyPropertyChanged
{
    // ...other fields and properties
    
    private string category;
    private SolidColorBrush categoryErrorBackgroundColor;

    public string Category
    {
        get
        {
            return category;
        }
        set
        {
            // validation checks
            if (value.Lenght < 5)
            {
                CategoryErrorBackgroundColor = Brushes.Red;
                // Notice that throwing is not even necessary for this solution to work
                throw new ArgumentException("Category cannot be shorter than 5 characters.");
            }
            else
            {
                CategoryErrorBackgroundColor = Brushes.Transparent;
            }
            category = value;
        }
    }
    // This is the property I'm binding to the cell's background
    // It has to have the appropriate type
    public SolidColorBrush CategoryErrorBackgroundColor
    {
        get
        {
            return categoryErrorBackgroundColor;
        }
        set
        {
            categoryErrorBackgroundColor = value;
            OnPropertyChanged();
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        var handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
}

Obviously, the huge downside of this solution is that it requires one (or more if you want more complex styles) style property for every property in the data object, and it needs a lot of manual settings of these properties. But it is kind of a solution nevertheless.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文