11

我仍在与细胞背景的操纵作斗争,所以我在问一个新问题。

用户“HB”写道,我实际上可以在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 的初学者。

解决方案第 1 部分:

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

<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',所以它应该可以工作,对吧?或者我做错了什么:| ?

解决方案第 2 部分:

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

感谢您尝试帮助我:)

4

3 回答 3

14

我可以为您的问题提出两种不同的解决方案:第一个是“代码隐藏样式”(您要求但我个人认为这不是 WPF 中的正确方法)和更多 WPF 样式(更棘手但保留代码- 在 clean 和使用样式、触发器和转换器的后面)

解决方案 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();
    }
}

UPD:如果您需要为整个数据网格着色,XAML 会容易得多(无需使用触发器)。使用以下单元格样式:

    <DataGrid.CellStyle>
            <Style TargetType="DataGridCell">
                 <Setter Property="Background" Value="{Binding RelativeSource={RelativeSource Self}, Path=Content.Text, Converter={StaticResource colorconverter}}"/>
            </Style>
    </DataGrid.CellStyle>
于 2013-05-20T10:27:48.783 回答
1

我的意思是您可以设置CellStyle列的属性,您不能直接操作单元格,因为它们在此事件中不可用。该样式可以包含以下形式的条件逻辑DataTriggers(将需要一个转换器,因为您有“小于”和不等于)和Setters.

此外,如果逻辑不是特定于列,您可以在网格本身上全局设置样式。使用该事件的目的是操作您无法访问的列属性。

于 2013-05-20T10:22:46.307 回答
-2

我不确定这个属性 (Cell.Style) 在您的 WPF Datagrid 中是否可用。在您的情况下可能存在一些替代方案。它适用于 WinForms 数据网格。

 cell.Style.BackColor = System.Drawing.Color.Black;
于 2013-05-20T09:47:05.507 回答