问题
We have such a scenario that we have a page including a DataGrid, and now we want to get all data from this DataGrid, but without accessing to the underlying item source of it, i.e., we want to access to the data directly from the DataGrid. It seems to be tricky but not impossible. I found many articles, like this: DataGridHelper, and this: Get WPF DataGrid row and cell, and many other ones. They are basically the same thing: to define the extension methods on DataGrid with help of another GetVisualChild function to find the target DataGridCell object. However, when I am using it, I can't find the target cell. Specifically, Each row in the DataGrid corresponds to one item from a collection of the DataContext, let's say, it is a collection of type "Employee", and each column of the DataGrid corresponds one property of class Employee, e.g, the Name, Gender, Age. Now my problem is, the above-mentioned GetCell() function always finds a DataGridCell with one Employee object as its content (the property of Content in DataGridCell), and can't go further into each property, no matter what column index I give it.
For example, in the GetCell function, there is one line:
Dim cell As DataGridCell = DirectCast(presenter.ItemContainerGenerator.ContainerFromIndex(column), DataGridCell)
,
where the presenter is a DataGridCellsPresenter I got which representing the row I choose, and as soon as I give the column index, naturally I am expecting it to return the control for selected property at position I specified. But it just doesn't work as expected. Any help would be appreciated!
回答1:
The moment you use presenter.ItemContainerGenerator.ContainerFromIndex
you fall into a limitation for it to work ONLY for non-virtualized items i.e. rows that are shown in the scroll view (plus some offset number of rows above and below the scroll view limits) of the datagrid.
For you to access values of all cells you will have to execute column level bindings for each row.
Access the
DataGrid.Items
collection. This is a view of items so any items hidden by filter criteria or custom paging etc will be excluded. If you dont want that then doDataGrid.ItemsSource.Cast<object>().ToList()
call.Now access all columns of the datagrid i.e.
DataGrid.Columns
. Assuming that they are of any type butDataGridTemplateColumn
, step 3 below will extract the cell level value. For template columns you will have to specify some property value that represents the entire template of the cell. I findDataGridTemplateColumn.SortMemberPath
a good candidate for this.Extract the
DataGridTextColumn.Binding
,DataGridCheckBoxColumn.Binding
,DataGridComboBoxColumn.SelectedValueBinding
orDataGridComboBoxColumn.SelectedItemBinding
. Then for each item from step 1, execute the binding to extract the value.
Code
private void Button_Click_1(object sender, RoutedEventArgs e)
{
string gridContent = string.Empty;
foreach(var item in MyDataGrid.Items)
{
foreach (var column in MyDataGrid.Columns)
{
var textCol = column as DataGridTextColumn;
var checkCol = column as DataGridCheckBoxColumn;
var comboCol = column as DataGridComboBoxColumn;
var templateCol = column as DataGridTemplateColumn;
if (textCol != null)
{
var propertyName = ((Binding)textCol.Binding).Path.Path;
var value
= item.GetType().GetProperty(
propertyName).GetValue(
item,
new object[] {});
if (((Binding)textCol.Binding).Converter != null)
{
value
= ((Binding)checkCol.Binding).Converter.Convert(
value,
typeof(object),
((Binding)checkCol.Binding).ConverterParameter,
((Binding)checkCol.Binding).ConverterCulture);
}
gridContent = gridContent + "\t" + value.ToString();
}
if (checkCol != null)
{
var propertyName = ((Binding)checkCol.Binding).Path.Path;
object value
= item.GetType().GetProperty(
propertyName).GetValue(
item,
new object[] { });
if (((Binding)checkCol.Binding).Converter != null)
{
value
= ((Binding)checkCol.Binding).Converter.Convert(
value,
typeof(object),
((Binding)checkCol.Binding).ConverterParameter,
((Binding)checkCol.Binding).ConverterCulture);
}
gridContent = gridContent + "\t" + value.ToString();
}
if (comboCol != null)
{
var propertyName = string.Empty;
if (comboCol.SelectedValueBinding != null)
{
propertyName
= ((Binding)comboCol.SelectedValueBinding).Path.Path;
}
else if (!string.IsNullOrEmpty(comboCol.SelectedValuePath))
{
propertyName = comboCol.SelectedValuePath;
}
else if (!string.IsNullOrEmpty(comboCol.DisplayMemberPath))
{
propertyName = comboCol.DisplayMemberPath;
}
var value = item.GetType().GetProperty(
propertyName).GetValue(
item,
new object[] { });
if (comboCol.SelectedValueBinding != null
&& ((Binding)comboCol.SelectedValueBinding).Converter != null)
{
var bnd = (Binding)comboCol.SelectedValueBinding;
value
= bnd.Converter.Convert(
value,
typeof(object),
bnd.ConverterParameter,
bnd.ConverterCulture);
}
gridContent = gridContent + "\t" + value.ToString();
}
if (templateCol != null)
{
var propertyName = templateCol.SortMemberPath;
var value
= item.GetType().GetProperty(
propertyName).GetValue(
item,
new object[] { });
gridContent = gridContent + "\t" + value.ToString();
}
}
gridContent = gridContent + "\n";
}
MessageBox.Show(gridContent);
}
}
回答2:
I realize this is old topic, but I searched for a simple solution and finally found it. Thought others might like simple. The following example searches the datagrid by specified column for the desired value, if found will select the row.
private void dgSetRow(DataGrid dg, string sColHeader, int iFindValue)
{
foreach (DataRowView drv in dg.Items )
{
// compare value in datarow of view
if (iFindValue == (int)drv.Row[sColHeader])
{
// select item
dg.SelectedItem = drv;
dg.ScrollIntoView(drv);
}
}
}
来源:https://stackoverflow.com/questions/7890076/access-to-cell-values-of-a-datagrid-in-wpf