WPF - How to get a cell from a DataGridRow?

2020-06-03 03:10发布

问题:

I have a data-bound DataGrid with alternating row background colors. I would like to color a cell differently based on the data it contains. I have tried the solution suggested by this thread

http://wpf.codeplex.com/Thread/View.aspx?ThreadId=51143

But,

DataGridCellsPresenter presenter = GetVisualChild(row)

always returns null.

I am using

    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++)
        {
            Visual v = (Visual)VisualTreeHelper.GetChild(parent, i);
            child = v as T;
            if (child == null)
            {
                child = GetVisualChild<T>(v);
            }
            if (child != null)
            {
                break;
            }
        }
        return child;
    }

But VisualTreeHelper.GetChildrenCount() of a DataGridRow always returns 0. I have verified that DataGridRow is not null and has been populated with data already. Any help is appreciated.

Thanks.

回答1:

If you know your row and index of the cell you'd like to access, then here's how you can do it in code:

//here's usage
var cell = myDataGrid.GetCell(row, columnIndex);
if(cell != null)
    cell.Background = Brushes.Green;

DataGrid Extension:

public static class DataGridExtensions
{
    public static DataGridCell GetCell(this DataGrid grid,  DataGridRow row, int columnIndex = 0)
    {
        if (row == null) return null;

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

        var cell = (DataGridCell)presenter.ItemContainerGenerator.ContainerFromIndex(columnIndex);
        if (cell != null) return cell;

        // now try to bring into view and retreive the cell
        grid.ScrollIntoView(row, grid.Columns[columnIndex]);
        cell = (DataGridCell)presenter.ItemContainerGenerator.ContainerFromIndex(columnIndex);

        return cell;
    }


回答2:

First thing first, don't do this in code-behind. You're fighting the framework with this way of doing things. WPF is designed differently; you have to think in terms of how the framework wants you to do things. In the case of WPF, it's XAML markup + converter classes.

You need two things to achieve what you want:

  • Proper XAML markup to setup the style of the DataGrid
  • An IValueConverter implementation to translate the value of the text into proper highlight color.

Here goes:

XAML In Your Datagrid

The first thing you want to do is define the XAML necessary to style your DataGrid cells. It looks like this:

<toolkit:DataGrid.CellStyle>
      <Style TargetType="{x:Type toolkit:DataGridCell}">
        <Style.Setters>
          <Setter Property="Background" Value="{Binding RelativeSource={RelativeSource Self}, Path=Content.Text, Converter={StaticResource dataGridCellConverter}}" />
        </Style.Setters>
      </Style>
    </toolkit:DataGrid.CellStyle>

What this is doing is setting up a binding to the RelativeSource (the DataGridCell) and telling it to use the Content.Text of the cell as the value to pass to the Converter (dataGridCellConverter).

IValueConverter

The next thing you need is an IValueConverter implementation to actually determine the colors based on the text of the cell:

using System;
using System.Globalization;
using System.Windows.Data;
using System.Windows.Media;
namespace UserControls.Utility.Converters
{
  public class DataGridCellConverter : IValueConverter
  {
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
      if (value == null) return Colors.White.ToString();

      if (value.ToString().ToUpper().Contains("CMS")) return "LIME";

      return "ORANGE";
    }

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

Here, I'm just looking for the text "CMS" and coloring the background cell; if "CMS" doesn't exist, then it returns the orange color instead.

Specify Resources

Now, you need to add markup in your window/usercontrol to specify the converter as an appropriate resource:

<UserControl.Resources>
    <Converters:DataGridCellConverter x:Key="dataGridCellConverter"/>
</UserControl.Resources>

And that should do it! Good luck.