There are no simple built-in methods for accessing individual rows or columns in WPF DataGrid. The following code samples will provide simple methods for accessing these items.
First of all we need a helper function for selecting a visual child:
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; }
There is a simple method for getting the current (selected) row of the DataGrid:
public static DataGridRow GetSelectedRow(this DataGrid grid) { return (DataGridRow)grid.ItemContainerGenerator.ContainerFromItem(grid.SelectedItem); }
We can also get a row by its indices:
public static DataGridRow GetRow(this DataGrid grid, int index) { DataGridRow row = (DataGridRow)grid.ItemContainerGenerator.ContainerFromIndex(index); if (row == null) { // May be virtualized, bring into view and try again. grid.UpdateLayout(); grid.ScrollIntoView(grid.Items[index]); row = (DataGridRow)grid.ItemContainerGenerator.ContainerFromIndex(index); } return row; }
Now we can get a cell of a DataGrid by an existing row:
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; }
Or we can simply select a row by its indices:
public static DataGridCell GetCell(this DataGrid grid, int row, int column) { DataGridRow rowContainer = grid.GetRow(row); return grid.GetCell(rowContainer, column); }
The functions above are extension methods. Their use is simple:
var selectedRow = grid.GetSelectedRow(); var columnCell = grid.GetCell(selectedRow, 0);
Is there a way to get the column selected ?
ReplyDeletemethods return null always
ReplyDeleteIt works for me. The reference implementation is on Google Code, see link above. That should work.
ReplyDeletePlease try to identify the line that causes the null value.
Thanks for this class. Great job!
ReplyDelete2 small comments:
1) the namespace DataGrid.Helpers cause a compiler error since "DataGrid" is a type.
Solution: Change the namespace to something
else (like DataGrids.Helpers)
2) "Methods return null always" : on my side the problem came from GetSelectedRow and GetRow(int index).
These functions return null if they are called BEFORE the grid have been (re-)displayed.
I was :
- adding a new item to an existing grid from code.
- set grid.SelectedItem=TheNewItem
- a call to grid.GetSelectedRow returned null
- a call to grid.GetRow returned null
I handle what I wanted to do by using grid.GetCell(Grid.SelectedIndex,ThecolumnNumber).
Thanks again for your code. Very helpful:)
Jacques
Thanks, but some questions:
ReplyDeleteWhen using loadingRow event, I can't get the cell (null) because there is no cell build yet. How can I set this (to be build) cell to change the color, font etc ? Any ideas?
this post really helped me thanks a ton!
ReplyDeleteArtur, very Thanks!!!
ReplyDeletedude.. you are the master!!!
ReplyDeleteGreat article helped alot. With this technique i was able to get all cells in my datagrid and custom style them the way i needed them. Thanks!
ReplyDeleteHallo,
ReplyDeleteI have the same problem like the person before:
Iam using loadingRow event on datagrid. Here the return value of the GetCell() Methode is always null!
How can i solve that?
Thanks
Being a bit more specific than my last question I would ask how do you not reference the static class with the extension method in it. In this case it would be "MyStaticClass.GetCell". My static class is in the same namespace as the rest of the code so I have no namespace reference but there is no "GetCell" method available without first referencing the static class.
ReplyDeleteThanks
The signature of the method:
ReplyDeletepublic static DataGridCell GetCell(this DataGrid grid, DataGridRow row, int column)
The *this DataGrid* part indicates you should call the extension method on the DataGrid instance you want to work with. So you should just call *myDataGridInstance.GetCell(row, column)*.
I hope I didn't misunderstand you question and it helps.
It helps very much. The error message I was seeing said there was no "GetCell" however a little farther into the text it referenced the extension method so I was a little mixed up. Now that I have the static class and methods I have the following in my code to use the class.
ReplyDelete" this.tbWeight.Text = TravelerdataGrid.GetCell(row, 1).ToString();'
I ma getting the following in my text box:
"System.Windows.Controls.DataGridCell"
I was expecting to see the value in the cell. Am I still missing something?
This is the way .NET FW works. If you don't override the ToString() function, it returns the class name.
ReplyDeleteYou should try calling TravelerdataGrid.GetCell(row, 1).Content() to get the content of the DataGridCell. See http://msdn.microsoft.com/en-us/library/system.windows.controls.contentcontrol.content.aspx
Hello,
ReplyDeleteOne question I cannot find property:
"ItemContainerGenerator" in my data grid object. Can you please tell me what wrong I am doing.
Thanks in advance.
Sonal Savaratkar
ItemContainerGenerator is a method to be found in objects of type DataGridCellsPresenter. However, If it is your intent to extract data from a cell in a DataGrid you will discover this can't be done with a WPF DataGrid. There are many partial demonstrations of how to do this but none actually assign the content of a cell to a text box or other variable. I would suggest exploring other commercially available controls.
ReplyDeleteThis is great, any way you could do a getSelectedCells function?
ReplyDeleteIf you examine Arturs' response about getting the content of the cell, it becomes evident that all of these functions produce the class if there is no reference to the content. I have found this to be true with all the functions I have tried even when the content is referenced. I believe I tried getSelectedCells with the same result but you definitely should try it for yourself. I don't recall the syntax or I would share it with you. I am looking into a product named Telerik that supposedly can handle this.
ReplyDeleteI was able to do this with my traveler object very easlly using WPF tools from Telerik and the following code ( I don't work for Telerik):
ReplyDeleteprivate void TravelerDetail_SelectionChanged(object sender, SelectionChangeEventArgs e)
{
Traveler row = this.TravelerDetail.SelectedItem as Traveler;
if(row.WeightCodeId != null)
this.tbWeight.Text = row.WeightCodeId.ToString();
if(row.RangeHigh != null)
this.tbRangeHigh.Text = row.RangeHigh.ToString();
if(row.RangeLow != null)
this.tbRangeLow.Text = row.RangeLow.ToString();
}
Thank you for your wonderful posting...
ReplyDeleteIf i want to load a Datagrid with my given (X,Y) coordinate value for the datagrid & a given value to show in Datagrid cell then how to..?
Use Grid.Column[x].GetCellContent(Row[y]) method
ReplyDeleteIt is much easier
I'm using .NET 3.5 [ thats my requirement ] and I dont find DataGridCellsPresenter in .NET 3.5. What is the alternative?
ReplyDeletegreat article....
ReplyDeleteReally good article, especially the cell selection part I was struggling with that.
ReplyDeleteKeep it up.
if the methods return null
ReplyDeleteput this code before you use them.
#region start the generator manually
IItemContainerGenerator generator = (YourDataGrid).ItemContainerGenerator;
GeneratorPosition position = generator.GeneratorPositionFromIndex(0);
using (generator.StartAt(position, GeneratorDirection.Forward, true))
{
foreach (object o in (YourDataGrid).Items)
{
DependencyObject dp = generator.GenerateNext();
generator.PrepareItemContainer(dp);
}
}
#endregion
Cell color will change in scrolling, unless EnableRowVirtualization = false is set
ReplyDelete