如何使用 MVVM 应用程序在 WPF 中以编程方式设置 DataGrid 的选定项?
Posted
技术标签:
【中文标题】如何使用 MVVM 应用程序在 WPF 中以编程方式设置 DataGrid 的选定项?【英文标题】:How to set selected item of a DataGrid programmatically in WPF with MVVM application? 【发布时间】:2013-03-27 09:48:52 【问题描述】:我已将DataTable
绑定到DataGrid
控件。如何以编程方式设置所选项目?
例子
在我的view model
中,我有一个 DataTable 类型的属性来绑定 DataGrid
private DataTable sizeQuantityTable;
public DataTable SizeQuantityTable
get
return sizeQuantityTable;
set
sizeQuantityTable = value;
NotifyPropertyChanged("SizeQuantityTable");
我的XAML
<DataGrid
ItemsSource="Binding SizeQuantityTable"
AutoGenerateColumns="True"
Margin="0,0,0,120" />
视图模型的constructor
(分配虚拟值)
this.SizeQuantityTable = new DataTable();
DataColumn sizeQuantityColumn = new DataColumn();
sizeQuantityColumn.ColumnName = "Size Quantity";
this.SizeQuantityTable.Columns.Add(sizeQuantityColumn);
DataColumn sColumn = new DataColumn();
sColumn.ColumnName = "S";
this.SizeQuantityTable.Columns.Add(sColumn);
DataColumn mColumn = new DataColumn();
mColumn.ColumnName = "M";
this.SizeQuantityTable.Columns.Add(mColumn);
DataRow row1 = this.SizeQuantityTable.NewRow();
row1[sizeQuantityColumn] = "Blue";
row1[sColumn] = "12";
row1[mColumn] = "15";
this.SizeQuantityTable.Rows.Add(row1);
DataRow row2 = this.SizeQuantityTable.NewRow();
row2[sizeQuantityColumn] = "Red";
row2[sColumn] = "18";
row2[mColumn] = "21";
this.SizeQuantityTable.Rows.Add(row2);
DataRow row3 = this.SizeQuantityTable.NewRow();
row3[sizeQuantityColumn] = "Green";
row3[sColumn] = "24";
row3[mColumn] = "27";
this.SizeQuantityTable.Rows.Add(row3);
好的。我创建了三列,即sizeQuantityColumn
、sColumn
和mColumn
,并添加了三行,即row1
、row2
和row2
。
所以,假设我想将选定的项目设置为row2
(所以在视图中,第二行应该突出显示)。
我该怎么做?
编辑
我将 DataGrid 的 SelectedIndex
硬编码为 1。(所以应该选择第二行)。在design time
中,它显示为选中。但不是在运行时。您可以在下面的快照中看到它。
所以最终问题是没有突出显示该行。
【问题讨论】:
【参考方案1】:有几种方法可以选择DataGrid
中的项目。这只是取决于哪一个最适合这种情况
首先也是最基本的是SelectedIndex
,这只会选择DataGrid
中该索引处的行
<DataGrid SelectedIndex="Binding SelectedIndex" />
private int _selectedIndex;
public int SelectedIndex
get return _selectedIndex;
set _selectedIndex = value; NotifyPropertyChanged("SelectedIndex");
SelectedIndex = 2;
SelectedItem
将选择与您设置的行匹配的行
<DataGrid SelectedItem="Binding SelectedRow" />
private DataRow _selectedRow;
public DataRow SelectedRow
get return _selectedRow;
set _selectedRow = value; NotifyPropertyChanged("SelectedRow");
SelectedRow = items.First(x => x.whatever == something);
最常见的是SelectedValue
和SelectedValuePath
设置,在这种情况下你设置你要选择的列,然后可以通过设置相应的值来选择行
<DataGrid SelectedValuePath="Size Quantity" SelectedValue="Binding SelectionValue"
private string _selectedValue
public string SelectionValue
get return _selectedValue;
set _selectedValue = value; NotifyPropertyChanged("SelectionValue");
SelectionValue = "Blue";
编辑:
这是我的测试,它突出显示很好
代码:
public partial class MainWindow : Window, INotifyPropertyChanged
public MainWindow()
InitializeComponent();
this.SizeQuantityTable = new DataTable();
DataColumn sizeQuantityColumn = new DataColumn();
sizeQuantityColumn.ColumnName = "Size Quantity";
...................
........
private string _selectedValue;
public string SelectionValue
get return _selectedValue;
set _selectedValue = value; NotifyPropertyChanged("SelectionValue");
private int _selectedIndex;
public int SelectedIndex
get return _selectedIndex;
set _selectedIndex = value; NotifyPropertyChanged("SelectedIndex");
private DataTable sizeQuantityTable;
public DataTable SizeQuantityTable
get return sizeQuantityTable;
set sizeQuantityTable = value; NotifyPropertyChanged("SizeQuantityTable");
private void Button_Click_1(object sender, RoutedEventArgs e)
SelectedIndex = 2;
private void Button_Click_2(object sender, RoutedEventArgs e)
SelectionValue = "Blue";
private void NotifyPropertyChanged(string p)
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(p));
Xaml:
<Window x:Class="WpfApplication21.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="202" Width="232" Name="UI">
<Grid DataContext="Binding ElementName=UI">
<DataGrid SelectedValuePath="Size Quantity"
SelectedValue="Binding SelectionValue"
SelectedIndex="Binding SelectedIndex"
ItemsSource="Binding SizeQuantityTable"
AutoGenerateColumns="True"
Margin="0,0,0,41" />
<StackPanel Orientation="Horizontal" Height="37" VerticalAlignment="Bottom" >
<Button Content="SelectedIndex" Height="26" Width="107" Click="Button_Click_1"/>
<Button Content="SelectedValue" Height="26" Width="107" Click="Button_Click_2"/>
</StackPanel>
</Grid>
</Window>
结果:
【讨论】:
非常感谢。我试图绑定SelectedItem
和SelectedValue
属性。但我无法完成我的任务。然后按照您的建议,我尝试使用SelectedIndex
。然后在设计时,它显示它是被选中的。但是在运行时它不会显示为选中状态。问题是即使它已被选中,该行也未突出显示。
我已经更新了这个问题。所以现在的问题是选中的行没有高亮
您是否使用Test
按钮设置SelectedIndex
?
号..号..Test
按钮做nothing
。 (我只用它来debug
目的)
确保在加载完所有内容后设置选择,尝试将选择代码放在测试按钮中,它应该会突出显示,如果有帮助,我还发布了我的测试应用程序:)【参考方案2】:
您始终可以使用SelectedItem
属性并将其绑定到行,如下所示:
SelectedItem="Binding ActiveRow"
在ViewModel
做:
ActiveRow = secondRow;
【讨论】:
我也试过这种方式。我定义了一个DataRowView
类型的属性并绑定到DataGrid 控件的SelectedItem
属性。这样我就可以get
选中的项目了。但是我couldn't set
选中的项目programmaticaly。
也许你忘了使用双向绑定。 SelectedItem="Binding ActiveRow, Mode=TwoWay" 怎么样?【参考方案3】:
在您的 DataGrid 中添加 SelectedItem、SelectedValue。
<DataGrid
ItemsSource="Binding SizeQuantityTable"
AutoGenerateColumns="True"
SelectedValue ="Binding SelectedValue"
Margin="0,0,0,120" />
在你的视图模型中
private string _selectedValue;
public string SelectedValue
get
return _selectedValue;
set
_selectedValue = value;
NotifyPropertyChanged("SelectedValue");
private DataTable sizeQuantityTable;
public DataTable SizeQuantityTable
get
return sizeQuantityTable;
set
sizeQuantityTable = value;
NotifyPropertyChanged("SizeQuantityTable");
您也可以使用 SelectedItem,这是首选。
【讨论】:
【参考方案4】:我也遇到了同样的问题。 我看到在设计时正确选择了数据网格的项目,但在运行时没有。 (顺便说一下,我在 xaml 中创建了视图模型的实例)。
我可以通过移动代码以编程方式将所选项目从视图模型构造函数设置到视图模型中的不同方法,然后在窗口(或用户控件)的加载事件中调用此方法来解决此问题。
很明显,当调用视图模型构造函数时,视图本身并没有完全完成初始化。 这可以通过不在视图模型构造函数中编码来避免。
查看(xaml):
<Window x:Class="MyWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Test"
xmlns:viewModel="clr-namespace:ViewModels" Loaded="Window_Loaded">
<Window.DataContext>
<viewModel:ExampleViewModel/>
</Window.DataContext>
查看(后面的代码):
private void Window_Loaded(object sender, RoutedEventArgs e)
((ExampleViewModel)this.DataContext).LoadData();
如果您不喜欢在后面的代码中设置Loaded
事件,您也可以在 xaml 中进行(需要引用“Microsoft.Expression.Interactions”和“System.Windows.Interactivity”):
<Window x:Class="MyWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Test"
xmlns:viewModel="clr-namespace:ViewModels">
<Window.DataContext>
<viewModel:ExampleViewModel/>
</Window.DataContext>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<ei:CallMethodAction TargetObject="Binding" MethodName="LoadData"/>
</i:EventTrigger>
</i:Interaction.Triggers>
在每种情况下,您都调用 ViewModel 中的 LoadData
方法:
public class ExampleViewModel
/// <summary>
/// Constructor.
/// </summary>
public ExampleViewModel()
// Do NOT set selected item here
public void LoadData()
// Set selected item here
【讨论】:
【参考方案5】:对于使用 Observable Collections 的任何人,您可能会发现此解决方案很有用。
SelectedModelIndex 属性仅返回 ItemSource 集合中 SelectedModel 的索引。我发现设置 SelectedIndex 和 SelectedItem 突出显示 DataGrid 中的行。
private ObservableCollection<Model> _itemSource
public ObservableCollection<Model> ItemSource
get return _itemSource;
set
_itemSource = value;
OnPropertyChanged("ItemSource");
// Binding must be set to One-Way for read-only properties
public int SelectedModelIndex
get
if (ItemSource != null && ItemSource.Count > 0)
return ItemSource.IndexOf(SelectedModel);
else
return -1;
private Model _selectedModel;
public Model SelectedModel
get return _selectedModel;
set
_selectedModel = value;
OnPropertyChanged("SelectedModel");
OnPropertyChanged("SelectedModelIndex");
【讨论】:
【参考方案6】:xaml:
<DataGrid SelectionUnit="FullRow" >
</DataGrid>
代码
SelectRowByIndex(yourDataGridViewName, 0);
public static void SelectRowByIndex(DataGrid dataGrid, int rowIndex)
if (!dataGrid.SelectionUnit.Equals(DataGridSelectionUnit.FullRow))
throw new ArgumentException("The SelectionUnit of the DataGrid must be set to FullRow.");
if (rowIndex < 0 || rowIndex > (dataGrid.Items.Count - 1))
throw new ArgumentException(string.Format("0 is an invalid row index.", rowIndex));
dataGrid.SelectedItems.Clear();
/* set the SelectedItem property */
object item = dataGrid.Items[rowIndex]; // = Product X
dataGrid.SelectedItem = item;
DataGridRow row = dataGrid.ItemContainerGenerator.ContainerFromIndex(rowIndex) as DataGridRow;
if (row == null)
/* bring the data item (Product object) into view
* in case it has been virtualized away */
dataGrid.ScrollIntoView(item);
row = dataGrid.ItemContainerGenerator.ContainerFromIndex(rowIndex) as DataGridRow;
//TODO: Retrieve and focus a DataGridCell object
if (row != null)
DataGridCell cell = GetCell(dataGrid, row, 0);
if (cell != null)
cell.Focus();
public static DataGridCell GetCell(DataGrid dataGrid, DataGridRow rowContainer, int column)
if (rowContainer != null)
DataGridCellsPresenter presenter = FindVisualChild<DataGridCellsPresenter>(rowContainer);
if (presenter == null)
/* if the row has been virtualized away, call its ApplyTemplate() method
* to build its visual tree in order for the DataGridCellsPresenter
* and the DataGridCells to be created */
rowContainer.ApplyTemplate();
presenter = FindVisualChild<DataGridCellsPresenter>(rowContainer);
if (presenter != null)
DataGridCell cell = presenter.ItemContainerGenerator.ContainerFromIndex(column) as DataGridCell;
if (cell == null)
/* bring the column into view
* in case it has been virtualized away */
dataGrid.ScrollIntoView(rowContainer, dataGrid.Columns[column]);
cell = presenter.ItemContainerGenerator.ContainerFromIndex(column) as DataGridCell;
return cell;
return null;
public static childItem FindVisualChild<childItem>(DependencyObject obj)
where childItem : DependencyObject
foreach (childItem child in FindVisualChildren<childItem>(obj))
return child;
return null;
public static IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj)
where T : DependencyObject
if (depObj != null)
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
if (child != null && child is T)
yield return (T)child;
foreach (T childOfChild in FindVisualChildren<T>(child))
yield return childOfChild;
【讨论】:
以上是关于如何使用 MVVM 应用程序在 WPF 中以编程方式设置 DataGrid 的选定项?的主要内容,如果未能解决你的问题,请参考以下文章