本文介绍了如何在 AutoGeneratingColumn 事件期间根据其值设置数据网格单元格的背景?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我仍在与细胞背景的操作作斗争,所以我提出了一个新问题.

I'm still fighting with manipulation of cell backgrounds so I'm asking a new question.

用户H.B."写道,我实际上可以在 期间设置单元格样式AutoGeneratingColumn 事件 - 根据值更改 DataGrid 单元格颜色.问题是我不知道该怎么做.

A user "H.B." wrote that I can actually set the cell style during the AutoGeneratingColumn event - Change DataGrid cell colour based on values. The problem is that I'm not sure how to do it.

我想要的:根据每个单元格的值设置不同的背景颜色.如果该值为 null,我还希望它不可可点击(我猜是可聚焦的).

What I want:Set different background colours for each cell depending on its value. If the value is null I also want it not to be clickable (focusable I guess).

我拥有的/我正在尝试做的:

private void mydatagrid_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
{
    foreach (Cell cell in e.Column)
    {
        if (cell.Value < 1)
        {
            cell.Background = Color.Black;
            cell.isFocusable = false;
        }
        else
        {
            cell.Background = Color.Pink;
        }
    }
}

这只是伪代码.在列自动生成过程中是否有可能发生这样的事情,如果是这样,我该如何编辑我的代码以使其有效?

This is just the pseudocode. Is something like this is possible during column auto-generation and if so, how can I edit my code so it will be valid?

我了解了值转换器,但我想知道它是否可以通过编程方式实现,而无需编写 XAML.

I read about value convertors but I want to know if it's somehow possible programmatically, without writing XAML.

请理解,我仍然是 C#/WPF/DataGrid 的初学者.

Please understand that I'm still a beginner to C#/WPF/DataGrid.

我使用了我接受的答案.把它放进

I used the answer I accepted. Just put it into

<Window.Resources>
<local:ValueColorConverter x:Key="colorConverter"/>
        <Style x:Key="DataGridCellStyle1" TargetType="{x:Type DataGridCell}">
            <Setter Property="Padding" Value="5"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type DataGridCell}">
                        <Border Padding="{TemplateBinding Padding}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" SnapsToDevicePixels="True">
                            <Border.Background>
                                <MultiBinding Converter="{StaticResource colorConverter}">
                                    <Binding RelativeSource="{RelativeSource AncestorType=DataGridCell}" Path="Content.Text"/>
                                    <Binding RelativeSource="{RelativeSource AncestorType=DataGridCell}" Path="IsSelected"/>
                                </MultiBinding>
                            </Border.Background>
                            <ContentPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
</Window.Resources>

并为它制作了一个 MultiBinding 转换器,因此我还可以为所选单元格设置背景颜色.

And made for it a MultiBinding convertor so I can also set the background color for selected cells.

现在我只需要解决设置空单元格焦点的问题.有什么提示吗?

Now I only have to solve the problem of setting focus of empty cells. Any hints?

  <Style.Triggers>
        <Trigger Property="HasContent" Value="False">
            <Setter Property="Focusable" Value="False"/>
        </Trigger>
    </Style.Triggers>

这不起作用.我在空单元格中有空字符串,但它们充满了null",所以它应该可以工作,对吗?或者我做错了什么:|?

This doesn't work. I had empty strings in the empty cells, but they are filled with ´null´s so it should work, right? Or what am I doing wrong :| ?

所以只要单元格值为TextBox",上面的代码就不起作用,所以我决定找到另一种方法来处理它,可以在我的答案中找到:https://stackoverflow.com/a/16673602/2296407

So the code above won't work as long as the cell value is a ´TextBox´ so I decided to find another way to deal with it which can be found in my answer here: https://stackoverflow.com/a/16673602/2296407

谢谢你帮助我:)

推荐答案

我可以为您的问题提出两种不同的解决方案:第一种是代码隐藏式"(您要求但我个人认为不是WPF 中的正确方法)和更多 WPF 风格(更棘手但保持代码隐藏干净并利用样式、触发器和转换器)

I can propose two different solutions for your question: the first is "code-behind-style" (which you are asking for but personally I think it is not right approach in WPF) and more WPF-style (which more tricky but keeps code-behind clean and utilizes styles, triggers and converters)

首先,您选择的方法不会直接起作用 - AutoGeneratingColumn 事件旨在用于改变整个列的外观,而不是逐个单元格的基础.因此,它可以用于,例如,根据其显示索引或绑定属性将正确的样式附加到整个列.

First of all, the approach you've chosen will not work directly - the AutoGeneratingColumn event is meant to be used for altering the entire column appearance, not on the cell-by-cell basis. So it can be used for, say, attaching the correct style to entire column basing on it's display index or bound property.

一般来说,第一次引发事件时,您的数据网格根本不会有任何行(因此 - 单元格).如果您确实需要捕获事件 - 请考虑使用 DataGrid.LoadingRow 事件.而且您将无法轻松获得细胞:)

Generally speaking, for the first time the event is raised your datagrid will not have any rows (and consequently - cells) at all. If you really need to catch the event - consider your DataGrid.LoadingRow event instead. And you will not be able to get the cells that easy :)

那么,您要做的是:处理 LoadingRow 事件,获取行(它具有 Item 属性,该属性保存(令人惊讶的是 :))您的绑定项目),获取绑定项目,制作所有需要的内容计算,得到需要修改的单元格,最后修改目标单元格的样式.

So, what you do: handle the LoadingRow event, get the row (it has the Item property which holds (surprisingly :)) your bound item), get the bound item, make all needed calculations, get the cell you need to alter and finally alter the style of the target cell.

这是代码(作为项目,我使用了一个带有用于着色的 intValue"属性的示例对象).

Here is the code (as item I use a sample object with the int "Value" property that I use for coloring).

XAML

   <DataGrid Name="mygrid" ItemsSource="{Binding Items}" AutoGenerateColumns="True" LoadingRow="DataGrid_OnLoadingRow"/>

.CS

    private void DataGrid_OnLoadingRow(object sender, DataGridRowEventArgs e)
    {
        Dispatcher.BeginInvoke(DispatcherPriority.Render, new Action(() => AlterRow(e)));
    }

    private void AlterRow(DataGridRowEventArgs e)
    {
        var cell = GetCell(mygrid, e.Row, 1);
        if (cell == null) return;

        var item = e.Row.Item as SampleObject;
        if (item == null) return;

        var value = item.Value;

        if (value <= 1) cell.Background = Brushes.Red;
        else if (value <= 2) cell.Background = Brushes.Yellow;
        else cell.Background = Brushes.Green;
    }

    public static DataGridRow GetRow(DataGrid grid, int index)
    {
        var row = grid.ItemContainerGenerator.ContainerFromIndex(index) as DataGridRow;

        if (row == null)
        {
            // may be virtualized, bring into view and try again
            grid.ScrollIntoView(grid.Items[index]);
            row = (DataGridRow)grid.ItemContainerGenerator.ContainerFromIndex(index);
        }
        return row;
    }

    public static T GetVisualChild<T>(Visual parent) where T : Visual
    {
        T child = default(T);
        int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
        for (int i = 0; i < numVisuals; i++)
        {
            var v = (Visual)VisualTreeHelper.GetChild(parent, i);
            child = v as T ?? GetVisualChild<T>(v);
            if (child != null)
            {
                break;
            }
        }
        return child;
    }

    public static DataGridCell GetCell(DataGrid host, DataGridRow row, int columnIndex)
    {
        if (row == null) return null;

        var presenter = GetVisualChild<DataGridCellsPresenter>(row);
        if (presenter == null) return null;

        // try to get the cell but it may possibly be virtualized
        var cell = (DataGridCell)presenter.ItemContainerGenerator.ContainerFromIndex(columnIndex);
        if (cell == null)
        {
            // now try to bring into view and retreive the cell
            host.ScrollIntoView(row, host.Columns[columnIndex]);
            cell = (DataGridCell)presenter.ItemContainerGenerator.ContainerFromIndex(columnIndex);
        }
        return cell;

    }

解决方案 2. WPF 风格

此解决方案仅将代码隐藏用于值到颜色的转换(假设着色逻辑比等式比较更复杂 - 在这种情况下,您可以使用触发器并且不要弄乱转换器).

Solution 2. WPF-style

This solution uses code-behind only for value-to-color convertions (assuming that that logic of coloring is more complex than equality comparison - in that case you can use triggers and do not mess with converters).

您的操作:使用包含数据触发器的样式设置 DataGrid.CellStyle 属性,该属性检查单元格是否在所需列内(基于它的 DisplayIndex),如果是 - 通过转换器应用背景.

What you do: set DataGrid.CellStyle property with style that contains a data trigger, which checks if the cell is within a desired column (basing on it's DisplayIndex) and if it is - applies background through a converter.

XAML

<DataGrid Name="mygrid" ItemsSource="{Binding Items}" AutoGenerateColumns="True">
        <DataGrid.Resources>
            <local:ValueColorConverter x:Key="colorconverter"/>
        </DataGrid.Resources>
        <DataGrid.CellStyle>
            <Style TargetType="DataGridCell">
                <Style.Triggers>
                    <DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=Column.DisplayIndex}" Value="1">
                        <Setter Property="Background" Value="{Binding RelativeSource={RelativeSource Self}, Path=Content.Text, Converter={StaticResource colorconverter}}"/>
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </DataGrid.CellStyle>
    </DataGrid>

.CS

public class ValueColorConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var str = value as string;
        if (str == null) return null;

        int intValue;
        if (!int.TryParse(str, out intValue)) return null;

        if (intValue <= 1) return Brushes.Red;
        else if (intValue <= 2) return Brushes.Yellow;
        else return Brushes.Green;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

UPD:如果您需要为整个数据网格着色,XAML 更容易(无需使用触发器).使用以下 CellStyle:

UPD: If you need to color entire datagrid, XAML is much easier (no need to use triggers). Use the following CellStyle:

    <DataGrid.CellStyle>
            <Style TargetType="DataGridCell">
                 <Setter Property="Background" Value="{Binding RelativeSource={RelativeSource Self}, Path=Content.Text, Converter={StaticResource colorconverter}}"/>
            </Style>
    </DataGrid.CellStyle>

这篇关于如何在 AutoGeneratingColumn 事件期间根据其值设置数据网格单元格的背景?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

07-30 19:46