Edgar
Edgar

Reputation: 489

Add more than 1 DataGridCell with style to a row in wpf dgv programatically

I have a class Ro that has 4 fields (2 names and 2 colors)

public class Ro
    {
        public string c1 { get; set; }
        public SolidColorBrush c1Color { get; set; }
        public string c2 { get; set; }
        public SolidColorBrush c2Color { get; set; }
    }

I have created a List of Ro objects

List<Ro> data = new List<Ro>();
            data.Add(new Ro()
            {
                c1 = "7B",
                c1Color = Brushes.Green,
                c2 = "",
                c2Color = Brushes.White
            });
            data.Add(new Ro()
            {
                c1 = "Jot",
                c1Color = Brushes.Green,
                c2 = "",
                c2Color = Brushes.Black
            });
            data.Add(new Ro()
            {
                c1 = "Nav",
                c1Color = Brushes.White,
                c2 = "",
                c2Color = Brushes.Orange
            });

Now I want to use this List to populate a wpf DataGridView assigning the color to each cell depending on current field of object when looping the list

To do so I created a method that will create ControlTemplate for each cell:

        public ControlTemplate CellTemplate(string text, SolidColorBrush color)
        {
            ControlTemplate template = new ControlTemplate();
            template.VisualTree = new FrameworkElementFactory(typeof(TextBlock));
            template.VisualTree.SetValue(TextBlock.TextProperty, text);
            template.VisualTree.SetValue(TextBlock.BackgroundProperty, color);
            template.VisualTree.SetValue(TextBlock.TextAlignmentProperty, TextAlignment.Center);
            template.VisualTree.SetValue(TextBlock.FontWeightProperty, FontWeights.Bold);
            if (color == Brushes.White)
                template.VisualTree.SetValue(TextBlock.ForegroundProperty, Brushes.Black);
            else
                template.VisualTree.SetValue(TextBlock.ForegroundProperty, Brushes.White);
            return template;
        }

and also I created a dataColumn c1

dataGrid1.Columns.Add(
    new DataGridTextColumn
    { Header = "c1" });

finally in a foreach loop I create the cells with styles

foreach (Ro me in data)
{
    DataGridCell cell0 = new DataGridCell { Template = CellTemplate(me.c1,me.c1Color) };
    dataGrid1.Items.Add(cell0);
  }

enter image description here

so far so good, however when I add the second column and try to apply the same idea like

 dataGrid1.Columns.Add(
    new DataGridTextColumn
    { Header = "c2" });

foreach (Ro me in data)
{
    DataGridCell cell0 = new DataGridCell { Template =    CellTemplate(me.c1,me.c1Color) };
    dataGrid1.Items.Add(cell0);

   DataGridCell cell1 = new DataGridCell { Template = CellTemplate(me.c2, me.c2Color) };
    dataGrid1.Items.Add(cell1);
}

i get excepction:

System.ArgumentOutOfRangeException was unhandled on System.Windows.Media.VisualCollection.get_Item(Int32 index) on System.Windows.Controls.UIElementCollection.get_Item(Int32 index) on System.Windows.Controls.UIElementCollection.System.Collections.IList.get_Item(Int32 index) on System.Windows.Controls.DataGridCellsPanel.ArrangeOverride(Size arrangeSize) ...

if I have only one column in dgv I get: enter image description here

I tried doing following, but got error, I do not know how to insert a particular cell in a particular column in dgv...

 foreach (Ro me in data)
            {
                DataGridCell cell0 = new DataGridCell { Template = CellTemplate(me.c1, me.c1Color) };
                DataGridCell cell1 = new DataGridCell { Template = CellTemplate(me.c2, me.c2Color) };
                dataGrid1.Columns.Insert(0, cell0);
                dataGrid1.Columns.Insert(1, cell0);
                //dataGrid1.Items.Add(cell0);
                //dataGrid1.Items.Add(cell1);
            }

How can I add cells in a row programatically?

Upvotes: 0

Views: 626

Answers (1)

CodingYoshi
CodingYoshi

Reputation: 27039

This is not possible without doing some gymnastics. Add your columns like this:

this.dataGrid.Columns.Add(
    new DataGridTextColumn
    { Header = "c1", Binding = new Binding("c1") } );

this.dataGrid.Columns.Add(
    new DataGridTextColumn
    { Header = "c2", Binding = new Binding("c2") });
this.dataGrid.LoadingRow += DataGrid_LoadingRow;

foreach (Ro me in data)
{
    dataGrid.Items.Add(me);
}

See how the code bounds the C1 column to c1 property of your Ro. See how the code also subscribes to the LoadingRow event. Here is the handler for the row which calls the AlterRow method asynchronously:

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

And here is the rest of the code which basically finds the cells in columns 0 and columns 1 for each row and then figures out the background property and sets it.

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

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

    cell.Background = item.c1Color;

    cell = GetCell(dataGrid, e.Row, 1);
    if (cell == null) return;

    cell.Background = item.c2Color;
}

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

Some of the code was borrowed form here.

Upvotes: 1

Related Questions