如何在AutoGeneratingColumn事件期间根据其值设置datagrid单元格的背景?

11

我还在努力处理单元格背景的操作,因此我想问一个新问题。

用户“H.B.”写道,我可以在AutoGeneratingColumn事件期间设置单元格样式-根据值更改DataGrid单元格颜色。问题是我不确定该如何做。

我想要的: 根据单元格的值设置不同的背景颜色。如果值为null,我也希望它不能点击(我猜可能是无法聚焦的)。

我有什么/我正在尝试做什么:

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;
        }
    }
}

这只是伪代码。类似这样的操作在列自动生成期间是否可行,如果可以,我该如何编辑我的代码使其有效?

我了解到值转换器,但我想知道是否可以通过编程方式实现,而不必编写XAML。

请理解我还是C#/WPF/DataGrid的新手。

解决方案第一部分:

我使用了我接受的答案。只需将它放入

<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 转换器,这样我就可以为选定的单元格设置背景颜色。

问题:

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

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

这不起作用。我在空单元格中有空字符串,但现在它们被填充了 ´null´ ,所以应该可以工作,对吧?或者我做错了什么 :| ?

解决方案第二部分:

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

感谢你试图帮助我 :)


你可以在Grid的RowDataBound事件中尝试这个。 - Waqar Majid
1
自动生成的单元格肯定有“内容”——它是<TextBlock>(这就是我在回答中使用Content.Text的原因):) 尝试使用<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=Content.Text}" Value="{x:Null}" - morincer
@morincer 感谢您的帮助。我设法以另一种方式处理空单元格,因为 DataTrigger 不想与我合作,而我已经非常生气了:D。但真的感谢您的支持。 - Ms. Nobody
@好友,没问题。不过我在你的答案中添加了一些代码风格的注释。 - morincer
更像是伪代码 = 伪代码 :)(我也有同样的问题,但我正在寻找代码后台的解决方案) - VisualBean
3个回答

14
我可以为您提供两种不同的解决方案:第一种是“代码后台样式”(您正在寻求的),但个人认为这不是WPF中正确的方法。第二种是更加WPF风格(更加棘手),但保持了代码后台的清晰,并利用了样式,触发器和转换器。
解决方案1.事件处理和代码后台逻辑着色
首先,您选择的方法将无法直接工作-AutoGeneratingColumn事件旨在用于更改整个列的外观,而不是基于单元格的基础。因此,它可以用于例如根据其显示索引或绑定属性将正确的样式附加到整个列。
一般来说,第一次引发事件时,您的数据网格将根本没有任何行(因此-单元格)。如果您确实需要捕获事件,请考虑使用DataGrid.LoadingRow事件。并且您将无法轻松获取单元格 :)
所以,您要做的是:处理LoadingRow事件,获取行(它具有保存(惊人地:))绑定项的Item属性),获取绑定项,进行所有必要的计算,获取需要修改的单元格,最后修改目标单元格的样式。
以下是代码(我使用一个带有int“Value”属性的示例对象作为项目,我用它来上色)。
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风格

这个解决方案仅在值与颜色转换时使用代码后台(假设着色逻辑比相等比较更为复杂 - 在这种情况下,可以使用触发器而不是转换器)。

你需要做的是:将DataGrid.CellStyle属性设置为包含数据触发器的样式,该触发器检查单元格是否在所需列中(基于其DisplayIndex),如果是,则通过转换器应用背景。

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();
    }
}

更新: 如果您需要对整个数据表格进行着色,使用XAML会更加容易(无需使用触发器)。请使用以下单元格样式:

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

1
你应该实现一个“is-less-than-converter”或类似的转换器,因为IsFocusable也应该被改变。 - H.B.
请您能否发布一段用于绑定和着色网格的代码(XAML + code-behind),这样我就可以查看哪里出了问题? - morincer
好的,如果有什么愚蠢的东西请不要笑。但问题是我在datagrid中需要的内容并不总是相同数量(行/列数),所以我用这些内容填充datatable,并将该表放入dataset中,因为它以某种方式起作用。我总是使用以下代码通过点击按钮填充数据网格:mydatagrid.ItemsSource = dataset.Tables[0].DefaultView; 它能工作,但我不确定如何将此类填充网格与背景颜色设置连接起来。我在xaml中没有写任何关于绑定的内容,只是这样做就行了。 - Ms. Nobody
该死,我想要给整个数据表格上色 :D - Ms. Nobody
已更新答案,包括完整网格着色的情况。 - morincer
显示剩余6条评论

1
我想表达的是,您可以设置列的 CellStyle 属性,但不能直接操作单元格,因为在此事件中不可用。样式可以包含条件逻辑,采用 DataTriggers 形式(需要转换器,因为您有“小于”而不是等于),以及 Setters
此外,如果逻辑与列无关,则可以全局设置 网格上的样式。使用事件的重点是操作列属性,否则您将无法访问它们。

-2

我不确定在你的WPF Datagrid中是否有这个属性(Cell.Style)。可能在你的情况下存在一些替代方案。它已经适用于WinForms datagrid。

 cell.Style.BackColor = System.Drawing.Color.Black;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接