Reputation: 18086
I tried to get row like this :
DataGridRow row = (DataGridRow)dataGrid.ItemContainerGenerator.ContainerFromIndex(i);
TextBlock cellContent = dataGrid.Columns[0].GetCellContent(row) as TextBlock;
But I only got null
. Is there another solution? What am I doing wrong?
I want to get data from my cells. My cells are checkboxes.
Upvotes: 5
Views: 49722
Reputation: 11
Maybe its will be usefull for somebody:
Before add item to DataGrid(WPF), if you want to get non-null DataGridRow in future, to change some properties like Background, you need to create new DataGridRow object, assign your class to DataGridRow.Item property and then add it to DataGrid.
Like this:
DataGridRow mRow = new DataGridRow();
mRow.Item = YOUR_DATA_CLASS;
_ = datagrid.Items.Add(mRow);
Upvotes: 1
Reputation: 155
DataGrid
to take on a suitable data object for manipulation:Since a DataGrid
operates on the principal that it is bound to a data object, you'll want to keep track of your data in something like a DataTable
.
For example, initialize a field for your MainWindow class of type DataTable and name it something relevant:
public partial class MainWindow : Window
{
private DataTable _cars = new DataTable("Cars");
Then in your Constructor, after you Initialize the Window component, tie the DataGrid.ItemSource
to the DataTable's collection as a data view:
public MainWindow()
{
InitializeComponent();
dgCars.ItemsSource = _cars.AsDataView();
}
Now, any time you programmatically add new rows to the _cars
table, they will be reflected inside the DataGrid
, yay! However, you want to be able to operate on the data from the user interface, so let's dive in!
DataTable
, using inputs from the user interface:When you want to operate on the data, you can get what is selected from the items inside the DataGrid
and use the indexes provided by them to remove the items from the DataTable
and then re-apply the DataView. That's the summary, but I'll go into more detail and finish the example:
We need to iterate over each DataGrid
item and check if it is selected before performing our logic:
for (int i = 0; i < dgCars.Items.Count; i++)
{
if (dgCars.SelectedItems.Contains(dgCars.Items[i]))
{
// This is where we do the magic
}
}
HOWEVER, we can't remove items from the DataTable
that is currently being used to supply the DataGrid
or we'll run into IndexOutOfBounds (and possibly Enumeration) errors, so to be safe, we'll use a copy of the table to operate on:
DataTable result = _cars.Copy(); //New in this step
for (int i = 0; i < dgCars.Items.Count; i++)
{
if (dgCars.SelectedItems.Contains(dgCars.Items[i]))
{
result.Rows.RemoveAt(i); //New in this step
}
}
Again, we'll run into IndexOutOfBounds errors, because we are iteration over the data as if there was X
amount of data, but each time we RemoveAt(i), we're now iterating over X--
amount of data. So, let's add a count and keep track:
int removed = 0; //New in this step
DataTable result = _cars.Copy();
for (int i = 0; i < dgCars.Items.Count; i++)
{
if (dgCars.SelectedItems.Contains(dgCars.Items[i]))
{
//Subtracting `removed` new in this step
result.Rows.RemoveAt(i - removed);
removed++; //New in this step
}
}
Last, but not least, we'll point our _cars
variable to our result
DataTable object on the heap and then reassign the dgCars.ItemSource = _cars.AsDataView()
to update our DataGrid (a more complicated explanation on this at the very bottom of my answer, if interested):
int removed = 0;
DataTable result = _cars.Copy();
for (int i = 0; i < dgCars.Items.Count; i++)
{
if (dgCars.SelectedItems.Contains(dgCars.Items[i]))
{
result.Rows.RemoveAt(i - removed);
removed++;
}
}
_cars = result; //New in this step
dgCars.ItemSource = _cars.AsDataView(); //New in this step
DataGrid
by selecting rows on it with your mouse and then clicking a button who's Click
value equals btnRemove_Click
. Simple modifications and logic change will allow you to do the same to add, edit, etc data, but with the principle we originally started with, which is to operate on the data object (in this case, a DataTable
) and have that item be the ItemsSource
for the DataGrid
.public partial class MainWindow : Window
{
private DataTable _cars = new DataTable("Cars");
public MainWindow()
{
InitializeComponent();
// THIS WASN'T IN THE BUILD EXAMPLE, BUT AS A BONUS:
// We could ALSO use this opportunity to setup static
// column headers if we know what they are in advance!
_cars.Columns.Add("Year");
_cars.Columns.Add("Make");
_cars.Columns.Add("Model");
dgCars.ItemsSource = _cars.AsDataView();
}
private btnRemove_Click(object sender, RoutedEventArgs e)
{
int removed = 0;
DataTable result = _cars.Copy();
for (int i = 0; i < dgCars.Items.Count; i++)
{
if (dgCars.SelectedItems.Contains(dgCars.Items[i]))
{
result.Rows.RemoveAt(i - removed);
removed++;
}
}
_cars = result;
dgCars.ItemSource = _cars.AsDataView();
}
}
Earlier in step 4, I mentioned:
Last, but not least, we'll point our
_cars
variable to ourresult
DataTable object on the heap and then reassign thedgCars.ItemSource = _cars.AsDataView()
to update our DataGrid
The reason for this is because _cars
and result
are both objects instantiated from a class, so they reside on the heap. Items on the heap are garbage collected (removed from memory) when there is no longer a reference to them on the stack. Since _cars
is a field of our MainWindow
, and continues outside the scope of btnRemove_Click
, when we point it to the DataTable result
we keep a reference to that table, and drop our reference to the original table. Thus, when btnRemove_Click
completes, the variable result
is garbage collected, the old DataTable
that _cars
USED to point to is garbage collected, and _cars
now references our new DataTable object that we created.
This answer goes into significantly more detail, and the comments on it are worth reading as well: https://stackoverflow.com/a/80113/13924556
Upvotes: 1
Reputation: 153
for(int row =0; row < dg_CountInventory.Rows.Count; row ++) //Loop through each row
{
//Provide the Column Index and row as in Loop
TextBlock b = dg_CountInventory.Columns[1].GetCellContent(dg_CountInventory.Items[row ]) as TextBlock;
}
dg_CountInventory is my Grid Name.This code will loop through all records present in Data-grid and Cell/Column Provided.#
Upvotes: -3
Reputation: 2323
This depends on how / when you are attempting to get this data. WPF is more geared towards accessing the data by the objects bound in the ItemsSource. So, if your ItemsSource is a List of MyObject, then the specific row will be of type MyObject instead of a pure DataRow.
If you are accessing the data by means of clicking on it, you can do something like this:
var currentItem = myDataGrid.SelectedItem as MyObject;
Now, you have the current MyObject in it's originally intended form rather than picking at the grid.
Upvotes: 2