6

All, I am attempting to loop through a WPF DataGrid using a for each loop to change the background colour of erroneous cells. I have checked many questions but I have yet to find a sufficient answer. What I have so far is

public void RunChecks()
{
    const int baseColumnCount = 3;
    foreach (DataRowView rv in dataGrid.Items)
    {
        for (int i = baseColumnCount; i < dataGrid.Columns.Count; i++)
        {
            if (!CheckForBalancedParentheses(rv.Row[i].ToString()))
            {
                Color color = (Color)ColorConverter.ConvertFromString("#FF0000");
                row.Background = new SolidColorBrush(color); // Problem!
            }
        }
    }
}

The problem is that in order to change the Background colour of a row in my DataGrid I need to work with the DataGridRow object ascociated with the DataRowView rv.

How do I get a reference to the DataGridRow from the object rv (DataRowView)?

Thanks for your time.

Edit. Based upon the advice below I now have the following style which works with the mouse over event and set the back and fore font of the relevant cell. However, I am really lost as to how to apply the backcolor to a cell at run-time in my code above. The XML style is

<Window.Resources>
    <Style TargetType="{x:Type DataGridRow}">
        <Style.Triggers>
            <Trigger Property="IsMouseOver"
                     Value="True">
                <Setter Property="Background" Value="Red" />
                <Setter Property="FontWeight" Value="ExtraBold" />
            </Trigger>
        </Style.Triggers>
    </Style>
</Window.Resources>
MoonKnight
  • 23,214
  • 40
  • 145
  • 277

5 Answers5

5

When you're working with WPF, avoid always accessing UI artifacts directly.

Create in you ModelView a property Color, and bind it to the background color of a single row template of your DataGrid view.

So in order to change the color you will loop throw the ModelView collecton and set/read Color property of every single object binded to every single row. By changing it, if binding is correctly settuped, you will affect row UI color appearance.

For concrete sample you can look on:

How do I bind the background of a data grid row to specific color?

Community
  • 1
  • 1
Tigran
  • 61,654
  • 8
  • 86
  • 123
  • 1
    Thanks for your time. I will take your advice and start reading. I have also been trying to colour and entire column, again with no luck. I have asked another question [here](http://stackoverflow.com/questions/15644105/change-the-background-color-of-entire-column-of-wpf-datagrid-at-runtime) if you can be borhered and want the rep. Thanks again... – MoonKnight Mar 27 '13 at 11:18
  • 1
    @Killercam: the idea is the same for rows and columns. If the UI object provied some property (color in this case) that can be changed either with direct access or via binding, you use binding from your modelview object. – Tigran Mar 27 '13 at 11:25
  • 1
    Thanks. Having read a chapter off a book on Styles, Triggers etc. I have now got the row changing color on a mouse over event. However, I am lost as to how to change a cell/row to the required color from the code at run-time using these style. if you can offer any help here it would be most appreciated... I have edited the question to show the style I have. – MoonKnight Mar 27 '13 at 12:29
  • 1
    @Killercam: look here: http://stackoverflow.com/questions/5030361/how-do-i-programatically-change-datagrid-row-color-in-wpf, hope this helps. It's not something, unfortunately, that can be described in simple answer in Q&A site. Define DataModel, ModelView, in model View color property, bind it in XAML, and at runtime access modelview objects and their properties to affect UI. – Tigran Mar 27 '13 at 12:58
2

You can use the ItemContainerGenerator to get the visual representation for your data i.e. DataGridRow -

DataGridRow row = (DataGridRow)dataGrid.ItemContainerGenerator
                                       .ContainerFromItem(rv);
Rohit Vats
  • 79,502
  • 12
  • 161
  • 185
  • 1
    This is returning `null` for the `row`. I have check `rv` and this is non-null. Sorry to bother you again with this, thanks for your time... – MoonKnight Mar 27 '13 at 11:17
  • 1
    This will return `null` only in case row is not rendered on your GUI yet. How many rows you are dealing with and is `virtualisation` enabled on your grid? – Rohit Vats Mar 27 '13 at 11:19
  • 1
    Link here - http://stackoverflow.com/questions/6713365/itemcontainergenerator-containerfromitem-returns-null might be of your help. – Rohit Vats Mar 27 '13 at 11:23
  • 1
    Currently anything from 1 row to 1000 rows, so not many in the scheme of things. I am not familiar with the `Visulisation` property of the `DataGrid`. I have tried to write this in XML but I can see a property with this name... Thanks again. – MoonKnight Mar 27 '13 at 11:23
  • 1
    Try setting this on your data grid `VirtualizingStackPanel.IsVirtualizing="False"` as mentioned in the link provided above. But, this has a trade-off that all 1000 rows will be rendered at a time. – Rohit Vats Mar 27 '13 at 11:27
  • 1
    Now I get "Unable to cast object of type 'MS.Internal.NamedObject' to type 'System.Data.DataRowView'." on the initialisation of the `foreach` loop... – MoonKnight Mar 27 '13 at 11:43
1

the answer of "Killercam" worked for me but i needed to add :

myDataGrid.UpdateLayout();

before using the GetCell methods so i'm sure i'm not getting a null reference.

to get the whole Helper class look at DataGridHelper

after all that pain, i try the whole code and i faced another probem wich is that the correctly colored cells changed during a scroll, the solution was to enable Virtualization and set its mode to Standard.

VirtualizingStackPanel.IsVirtualizing="True"
VirtualizingStackPanel.VirtualizationMode="Standard"

so, hope this will help any one who will need to iterate through datagrid cells.

MSTr
  • 223
  • 1
  • 7
0

After much reading I have found a way to do what I wanted - colour cells at run-time depending on certain conditions. This is the method I use to do the colouring

public void RunChecks()
{
    const int baseColumnCount = 3;
    for (int i = baseColumnCount; i < dataGrid.Columns.Count; i++)
    {
        foreach (DataGridRow row in Utilities.GetDataGridRows(dataGrid))
        {
            if (row != null)
            {
                DataGridCell cell = dataGrid.GetCell(row, i);

                // Start work with cell.
                Color color;
                TextBlock tb = cell.Content as TextBlock;
                string cellValue = tb.Text;
                if (!CheckForBalancedParentheses(cellValue))
                    color = (Color)ColorConverter.ConvertFromString("#FF0000");
                else
                    color = (Color)ColorConverter.ConvertFromString("#FFFFFF");
                row.Background = new SolidColorBrush(color);
                //cell.Background = new SolidColorBrush(color);
            }
        }
    }
}

These are the associated utility methods required

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

public static DataGridCell GetCell(this DataGrid grid, DataGridRow row, int column)
{
    if (row != null)
    {
        DataGridCellsPresenter presenter = GetVisualChild<DataGridCellsPresenter>(row);
        if (presenter == null)
        {
            grid.ScrollIntoView(row, grid.Columns[column]);
            presenter = GetVisualChild<DataGridCellsPresenter>(row);
        }
        DataGridCell cell = (DataGridCell)presenter.ItemContainerGenerator.ContainerFromIndex(column);
        return cell;
    }
    return null;
}

public static IEnumerable<DataGridRow> GetDataGridRows(DataGrid grid)
{
    var itemsSource = grid.ItemsSource as IEnumerable;
    if (null == itemsSource) yield return null;
    foreach (var item in itemsSource)
    {
        var row = grid.ItemContainerGenerator.ContainerFromItem(item) as DataGridRow;
        if (null != row) yield return row;
    }
}

Note however, that the colour of the cells move from cell to cell when the user scrolls!? This is yet another amazing annoyance of WPF. I am really confused over why something this simple can be made so so difficult. The suggestion that we need to use a MVVM-type pattern for this kind of thing I find amazing...

I hope this helps someone else.

MoonKnight
  • 23,214
  • 40
  • 145
  • 277
0
//the Class that resembles the Datagrid Item schema

foreach (Class cl in dgDatagrid.Items)
   {
       MessageBox.Show(cl.ID.ToString());
   }

this is the most simplest way to read the datagrid data
most articles misguide such simple things
yudhi
  • 1