20

Using WPF C#.NET4.5 using visual studio 2012 ulti.

Old winforms code:

foreach (DataGridViewRow paretoRow in ParetoGrid.Rows)
{
       if ((Convert.ToInt32(paretoRow.Cells["CurrentPareto"].Value) < (Convert.ToInt32(paretoRow.Cells["NewPareto"].Value))))
       {
              paretoRow.Cells["pNew"].Value = downArrow
       }
}

As you can see each row I cycle through I check a specific cell, if true I then populate another cell. This was good old winforms code I used many times before...however. Switching over to WPF was alot more different than i previously assumed.

DataGrid does not contain the Row property. Instead, I think you need to use:

DataGridRow paretoRow in paretogrid.Items

But im still at a loss on who to now get the cell.

So my question is, is there syntax changes to perform, if so where? Or as I'm beginning to believe datagrids in WPF operate with Objects more so than winforms thus not needing to use a propertie called "row", if this is the case what logic/syntax should i know use in this example?

Thanks for your patience guys, think when I go home for the bank holiday I'll do a bit of WPF digging to see how different it actually is.

Dave Clemmer
  • 3,741
  • 12
  • 49
  • 72
lemunk
  • 2,616
  • 12
  • 57
  • 87

9 Answers9

32

People seem to be overcomplicating this, this worked for me:

foreach (System.Data.DataRowView dr in yourDataGrid.ItemsSource)
{
     MessageBox.Show(dr[0].ToString());
}
Dan Cundy
  • 2,649
  • 2
  • 38
  • 65
Charles Clayton
  • 17,005
  • 11
  • 87
  • 120
28

I think first think you want to do is to get all rows of your DataGrid:

public IEnumerable<Microsoft.Windows.Controls.DataGridRow> GetDataGridRows(Microsoft.Windows.Controls.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 Microsoft.Windows.Controls.DataGridRow;
        if (null != row) yield return row;
    }
}

and then iterate through your grid:

var rows = GetDataGridRows(nameofyordatagrid); 

foreach (DataGridRow row in rows)  
{  
  DataRowView rowView = (DataRowView)row.Item;
  foreach (DataGridColumn column in nameofyordatagrid.Columns)
  {
      if (column.GetCellContent(row) is TextBlock)
      {
          TextBlock cellContent = column.GetCellContent(row) as TextBlock;
          MessageBox.Show(cellContent.Text);
      }
  } 
meJustAndrew
  • 6,011
  • 8
  • 50
  • 76
COLD TOLD
  • 13,513
  • 3
  • 35
  • 52
  • well how do i get all rows? looking at my foreach loop i used to use datgridviewrow hwoever that doesnt work for WPF how would you get all rows ? – lemunk Mar 28 '13 at 16:26
  • thanks for the code ill give it a whirl. still at first it looks so long winded compared to winforms a simple 3 liner becomes alot more complex. but i suppose this a fix to my current approach, which shud be the change. am I right in thinking this? – lemunk Mar 28 '13 at 16:31
  • got a syntax red liner under var itemsource = grid.itemsource as ienumerable. the error is the alias "IEnumerable" saying "requires 1 type arguement" – lemunk Mar 28 '13 at 16:39
  • you need to make sure yoiu have itemsource in your grid – COLD TOLD Mar 28 '13 at 16:43
  • 1
    "you need to make sure you have itemsource in your grid" what do you mean by this? please be patient with me lol – lemunk Mar 28 '13 at 16:46
  • 1
    Very Usefull, but since the march 2009 toolkit release of silverlight changed the namespaces microsoft.windows.controls to system.windows.controls : http://silverlight.codeplex.com/wikipage?title=Silverlight%20Toolkit%20March%202009%20change%20list the code will change a little bit and you will need to have : using System.Collections.Generic; using System.Collections; To fix the error of "IEnumerable" saying "requires 1 type arguement" – Mr Rubix Feb 01 '16 at 18:04
1

Yes, you are right. WPF DataGrid is built around better supporting the use of objects.

You could use a ViewModel similar to the following. Build them all into a collection and then set that collection as your ItemsSource. You would also need to use a ValueConverter if you want to display and image instead of a checkmark for pNew being true/false.

public class FooViewModel : INotifyPropertyChanged
{
    private int currentPareto;
    public int CurrentPareto 
    {
        get
        {
           return currentPareto;
        }
        set
        { 
            if (currentPareto == value)
                return;

            currentPareto = value;
            OnPropertyChanged("CurrentPareto");
            OnPropertyChanged("pNew");
        }
    }

    private int newPareto;
    public int NewPareto 
    {
        get
        {
           return newPareto;
        }
        set
        { 
            if (newPareto == value)
                return;

            newPareto = value;
            OnPropertyChanged("NewPareto");
            OnPropertyChanged("pNew");
        }
    }

    public bool pNew
    {
        get
        {
            return CurrentPareto < NewPareto;
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    public void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

Edit

To simplify it a little, you could use a base ViewModel class and use PropertyChanged weaving. The code would simplify to this:

public class FooViewModel : ViewModelBase
{
    public int CurrentPareto { get; set; }
    public int NewPareto { get; set; }
    public bool pNew { get { return CurrentPareto < NewPareto; } }
}
Nick Freeman
  • 1,411
  • 1
  • 12
  • 25
1

I don't even understand why is it just so complicated to get rows and their values in a datagrid. It feels like hell finding how. The api even give funny funny event names which is not so direct to the point also. Why can't just people concentrate on the baseline and give what exactly is needed and not all sorts of different options with no use and sense at all. I mean to eat all you need is a spoon and fork right. Never even changed since 100,000 years ago. This is my code thanks to the guy who mentioned some people just try to over-complicate things and waste your time.

    private void dtaResultGrid_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
    {
        ActivateTestDatagridAccess();
    }

    public async void ActivateTestDatagridAccess()
    {
        try
        {
            await Task.Delay(500);
            foreach (System.Data.DataRowView dr in dtaResultGrid.ItemsSource)
            {
                for (int j = 0; j < dtaResultGrid.Columns.Count; j++)
                {
                    Console.WriteLine(dr[j].ToString());
                }
                Console.Write(Environment.NewLine);
            }
        }
        catch (Exception exrr)
        {
            Console.WriteLine(exrr.ToString());
        }
    }
1

The 'simplest' answer, from Charles, did it for me. But I used Items instead of ItemsSource.

Now, for people getting this error:

System.InvalidCastException
Unable to cast object of type 'MS.Internal.NamedObject' to type 'System.Data.DataRowView'.

What did it for me was disabling the DataGrid's property CanUserAddRows. This removes the placeholder line for a new line, thus removing the placeholder object (which is NOT a DataRowView, but something else). If you already have this disabled, then I don't know.

Since I wanted to loop through each element of each row, I added another foreach:

foreach (System.Data.DataRowView dr in nameofyourgrid.Items)
{
    foreach (var item in dr.Row.ItemArray)
    {
        MessageBox.Show(item.ToString());
    }
}
0

In WPF you go about it a lot more dynamic and ObjectOrientated. You can bind the Column "pNew" on a Property of the element you put in the DataGrid, which returns downarrow. If the value changes you can raise the Event PropertyChanged (Interface INotifyPropertyChanged) and the bound Property will get reevaluated.

Also interesting for beginning with WPF is DataTemplate, ControlTemplate, Converter. Converter changes the Property Value to a usable Value for WPF (e.g. BoolToVisibility) when the Property gets called. DataTemplate and ControlTemplate can be used to alter the appearance of the Control.

There are several good Tutorials for WPF out there. I would also recommend to look into the MVVM-Pattern to use as a between layer of your Businessobject and your WPF-Control, especially to handle things like what you try to do here.

Dave Clemmer
  • 3,741
  • 12
  • 49
  • 72
Patrick
  • 264
  • 2
  • 11
  • thanks for the advice thats definatly something im looking into this weekend. At the moment im looking for a sort of "hack" to this solution, just to see how you could do it using a winforms style approach. – lemunk Mar 28 '13 at 16:29
0

if you fill your datagridview rows using an instance of a class (like struct_class) this would be the fastest way to have a foreach loop

foreach (struct_class row in dgv.Items)
{
    MessageBox.Show(row.name);
}
Saghachi
  • 851
  • 11
  • 19
0

If your DataGrid is bound to a custom object collection (e.g. List<MyViewItem>) the DataGridRow iteration obviously does not work. Only if you use DataTable/DataGridView as DataGrid ItemsSource.

You can iterate your custom DataGrid collection but only the rows in view have an evaluated binding.

To solve this I used the BindingPropertyHelper solution as Fallback:

public static class PropertyPathHelper
{
    public static object GetValue(object obj, string propertyPath)
    {
        Binding binding = new Binding(propertyPath);
        binding.Mode = BindingMode.OneTime;
        binding.Source = obj;
        BindingOperations.SetBinding(_dummy, Dummy.ValueProperty, binding);
        return _dummy.GetValue(Dummy.ValueProperty);
    }

    private static readonly Dummy _dummy = new Dummy();

    private class Dummy : DependencyObject
    {
        public static readonly DependencyProperty ValueProperty =
            DependencyProperty.Register("Value", typeof(object), typeof(Dummy), new UIPropertyMetadata(null));
    }
}

The following code converts all DataGrid rows to a new (iteratable) DataTable:

DataTable dt = new DataTable();
for (int i = 0; i < dataGrid.Columns.Count; i++)
{
    var c = dataGrid.Columns[i];

    if (c.Header != null)
        dt.Columns.Add(new DataColumn(c.Header.ToString()));
    else
        dt.Columns.Add();
}

for (int rowIndex = 0; rowIndex < dataGrid.Items.Count; rowIndex++)
{
    var item = dataGrid.Items[rowIndex];

    var newRow = dt.NewRow();

    bool written = false;
    for (int columnIndex = 0; columnIndex < dataGrid.Columns.Count; columnIndex++)
    {
        DataGridColumn column = dataGrid.Columns[columnIndex];

        FrameworkElement feColumn = column.GetCellContent(item);
        if (feColumn is TextBlock tb)
        {
            // binding loaded (in view)
            newRow[columnIndex] = tb.Text;
            written = true;
        }
        else if (column is DataGridTextColumn textColumn
            && textColumn.Binding is Binding bind)
        {
            // falback and evaluate binding explicit (out of view)
            object value = PropertyPathHelper.GetValue(item, bind.Path.Path);
            newRow[columnIndex] = value;
            written = true;
        }
    }

    if (written == false)
    {
        throw new InvalidOperationException($"Could not read row index {rowIndex} of DataGrid; Source item type: {item?.GetType()?.FullName}");
    }

    dt.Rows.Add(newRow);
}
Benni
  • 203
  • 2
  • 7
-4

Why can't you just use this property to get the number of rows and then use a For loop to iterate through?

dataGridView1.Rows.Count
ChibaKun
  • 77
  • 2
  • 11
  • Your answer is correct (so I gave it an upvote). Maybe most of the downvoters probably wanted more example code? (I wish downvoting required a comment.) – Jeff Sep 09 '19 at 19:19
  • 3
    This answer is not correct because the WPF DataGrid does not have a member named Rows. This is actually mentioned previously in this thread. But I also wish downvoting required a comment. – Dennis Dec 15 '19 at 21:03