【发布时间】:2010-07-22 10:39:09
【问题描述】:
我有一个绑定到可观察集合的数据网格。 当用户按下“添加按钮”时,它会添加一个新行,我通过向 observablecollection 添加一个新元素来做到这一点。
我不知道如何使新添加的行使第一个单元格成为焦点,就好像我们正在编辑一样。我正在使用 MVVM 模式。
有什么想法或建议吗?
【问题讨论】:
标签: wpf
我有一个绑定到可观察集合的数据网格。 当用户按下“添加按钮”时,它会添加一个新行,我通过向 observablecollection 添加一个新元素来做到这一点。
我不知道如何使新添加的行使第一个单元格成为焦点,就好像我们正在编辑一样。我正在使用 MVVM 模式。
有什么想法或建议吗?
【问题讨论】:
标签: wpf
The answer given by Gauss是正确的做法,但是加上一些代码,就更清楚了:
void DataGrid_LoadingRow(object sender, DataGridRowEventArgs e)
{
e.Row.Loaded += Row_Loaded;
}
void Row_Loaded(object sender, RoutedEventArgs e)
{
var row = (DataGridRow) sender;
row.Loaded -= Row_Loaded;
DataGridCell cell = GetCell(dataGrid, row, 0);
if (cell != null) cell.Focus();
dataGrid.BeginEdit();
}
static DataGridCell GetCell(DataGrid dataGrid, DataGridRow row, int column)
{
if (dataGrid == null) throw new ArgumentNullException("dataGrid");
if (row == null) throw new ArgumentNullException("row");
if (column < 0) throw new ArgumentOutOfRangeException("column");
DataGridCellsPresenter presenter = FindVisualChild<DataGridCellsPresenter>(row);
if (presenter == null)
{
row.ApplyTemplate();
presenter = FindVisualChild<DataGridCellsPresenter>(row);
}
if (presenter != null)
{
var cell = presenter.ItemContainerGenerator.ContainerFromIndex(column) as DataGridCell;
if (cell == null)
{
dataGrid.ScrollIntoView(row, dataGrid.Columns[column]);
cell = presenter.ItemContainerGenerator.ContainerFromIndex(column) as DataGridCell;
}
return cell;
}
return null;
}
static T FindVisualChild<T>(DependencyObject obj) where T : DependencyObject
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(obj, i);
var visualChild = child as T;
if (visualChild != null)
return visualChild;
var childOfChild = FindVisualChild<T>(child);
if (childOfChild != null)
return childOfChild;
}
return null;
}
您检索了DataGrid.LoadingRow 事件中的行,但该单元格尚不可用。因此,您在Loaded 事件上放置了一个处理程序,以等待此事件发生,然后您可以检索单元格并将焦点放在它上面。
删除处理程序以避免新的触发。
编辑会话也可以使用dataGrid.BeginEdit() 开始。
所有这些都在代码隐藏中,因为它属于视图。
【讨论】:
尝试捕获 DataGrid 的 LoadingRow(或类似)事件。在行上做一个SetFocus(e.Row)(或类似的)。这纯粹是面向视图的,所以符合MVVM。
【讨论】:
你想做这样的事情:
DataGridCell cell = GetCell(rowIndex, colIndex);
cell.Focus;
当然,您需要那种难以捉摸的 GetCell() 方法。来自 MSFT 的 Vincent Sibal 在this forum post 中编写了该方法(以及所需的 GetRow)。当我遇到这个时,我正在寻找其他东西,所以我没有使用它,但其他人似乎对它很幸运。您会注意到有指向 his blog 的链接,您可能还会发现这些链接对所有与 DataGrid 相关的事情都有帮助。
【讨论】:
希望对其他人有所帮助。
我将在视图中展示 MVVM 方式:
<DataGrid Name="dg1" ItemsSource="{Binding Table}" AutoGenerateColumns="False" Margin="3" SelectionMode="Single" IsReadOnly="True" SelectedIndex="{Binding Path=SelectedIndex, Mode=TwoWay}">
<DataGrid.Columns>
<DataGridTextColumn Header="Date" Binding="{Binding mydate}" MinWidth="100"></DataGridTextColumn>
<DataGridTextColumn Header="Count" Binding="{Binding Count}" MinWidth="100"></DataGridTextColumn>
</DataGrid.Columns>
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged" SourceName="dg1" >
<i:InvokeCommandAction Command="{Binding DgSelectionChanged}" CommandParameter="{Binding ElementName=dg1}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</DataGrid>
现在在视图模型中:
您需要在添加、删除等之后设置 SelectedIndex ......并且
private ICommand dgSelectionChanged;
/// <summary>
/// when the datagrid Selection Changes
/// </summary>
public ICommand DgSelectionChanged
{
get
{
return dgSelectionChanged ??
(dgSelectionChanged = new RelayCommand<DataGrid>(dg1 =>
{
// whatever you want when the Selection Changes
SelectedIndex= d
//select the item
if (dg1.SelectedIndex > -1)
{
dg1.Focus();
dg1.CurrentCell = new DataGridCellInfo(dg1.Items[dg1.SelectedIndex], dg1.Columns[0]);
}
}));
}
}
顺便说一句,要在控件加载后选择第一行,请将其添加到视图中:
<!-- select the row after control loaded -->
<i:EventTrigger EventName="Loaded" SourceName="dg1" >
<i:InvokeCommandAction Command="{Binding DgSelectionChanged}" CommandParameter="{Binding ElementName=dg1}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
谢谢,阿维。
【讨论】:
对我有用的是LoadingRow、e.Row.Loaded 和以下链接的GetCell() 方法的组合:
DataGrid.LoadingRow 事件在单元格通过GetCell 可用之前被调用。但在DaraGridRow.Loaded 事件中,单元格可用。
之后,您可以使用cell.Focus() 和DataGrid.BeginEdit()。
【讨论】:
感谢 Jake Berger 的回答,解决了我的问题。
绑定 DataGrid 的 SelectedItem 并处理 LoadingRow 事件效果很好。
例如:
<DataGrid SelectionUnit="FullRow" SelectionMode="Single" ItemsSource="{Binding Source=MyList}" SelectedItem="{Binding SelectedDataItem, Mode=TwoWay}" LoadingRow="MyDataGrid_LoadingRow"></DataGrid>
private void MyDataGrid_LoadingRow(object sender, DataGridRowEventArgs e)
{
SelectedDataItem = (MyDataObject)e.Row.Item;
}
【讨论】: