c# - 当 observablecollection ItemSource 更改 WPF 时,通过 MVVM 以编程方式关注行中的特定单元格
问题描述
我正在尝试将焦点设置在 DataGrid 中一行中的特定单元格上。数据网格绑定到 X 的可观察集合。当我点击特定按钮时,它会导致将新的 X 插入到可观察集合中,但是数据网格方面没有焦点,我想聚焦光标所以您可以输入新创建的行(行中的特定单元格)。这可能会走 MVVM 路线吗?我的虚拟机有可观察的集合,所以不太确定从这里去哪里......
解决方案
我想出了一个简单的例子
1) MVVM 辅助类(用于可见性):
public class NotifyPropertyChanged : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName]string propertyName = null)
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public class RelayCommand : ICommand
{
private readonly Action<object> _execute;
private readonly Func<object, bool> _canExecute;
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public RelayCommand(Action<object> execute, Func<object, bool> canExecute = null)
{
_execute = execute;
_canExecute = canExecute;
}
public bool CanExecute(object parameter) => _canExecute == null || _canExecute(parameter);
public void Execute(object parameter) => _execute(parameter);
}
2) 标记
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="30"/>
<RowDefinition/>
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal" Margin="5">
<Button Content="Add row" Command="{Binding AddRowCommand}"/>
</StackPanel>
<DataGrid ItemsSource="{Binding ItemsList}" Grid.Row="1" AutoGenerateColumns="False" LoadingRow="DataGrid_LoadingRow" CanUserAddRows="False" >
<DataGrid.Columns>
<DataGridTextColumn Header="Column 1" Binding="{Binding Column1}"/>
<DataGridTextColumn Header="Column 2" Binding="{Binding Column2}"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
3) ViewModel 和数据
public class MainViewModel : NotifyPropertyChanged
{
private ObservableCollection<DataItem> _itemsList;
private ICommand _addRowCommand;
public ObservableCollection<DataItem> ItemsList
{
get => _itemsList;
set
{
_itemsList = value;
OnPropertyChanged();
}
}
public ICommand AddRowCommand => _addRowCommand ?? (_addRowCommand = new RelayCommand(parameter =>
{
ItemsList.Add(new DataItem());
}));
public MainViewModel()
{
ItemsList = new ObservableCollection<DataItem>
{
new DataItem { Column1 = "aaa", Column2 = "bbb" },
new DataItem { Column1 = "ccc", Column2 = "ddd" }
};
}
}
public class DataItem : NotifyPropertyChanged
{
private string _column1;
private string _column2;
public string Column1
{
get => _column1;
set
{
_column1 = value;
OnPropertyChanged();
}
}
public string Column2
{
get => _column2;
set
{
_column2 = value;
OnPropertyChanged();
}
}
}
4)和代码隐藏类中的事件处理程序
private void DataGrid_LoadingRow(object sender, DataGridRowEventArgs e)
{
DataGrid dataGrid = sender as DataGrid;
// condition avoids execution when DataGrid initially loading
// and case when DataGrid adding an empty row if UserCanAddRows is enabled
if (dataGrid.IsLoaded && e.Row.Item is DataItem)
{
dataGrid.Focus(); // moving focus from button to DataGrid
dataGrid.SelectedIndex = e.Row.GetIndex(); // highlighting current row as selected, just a visual thing, you may remove it
dataGrid.CurrentCell = new DataGridCellInfo(e.Row.Item, dataGrid.Columns[0]); // set current cell
Dispatcher.BeginInvoke((Action)(() =>
{
dataGrid.BeginEdit(); // to avoid focus issue I'm running this through Dispatcher asynchronously
}));
}
}
这不会破坏 MVVM,因为事件处理程序不与数据交互而只检查其类型。也许有一种更安全的方法来检查它是否NewItemPlaceholder
不在e.Row.Item
. 或者您可以删除类型检查以防万一CanUserAddRows
设置为 false。
推荐阅读
- bash - 如何通过bash将文件所有者mod编号复制到文件组?
- javascript - 使用 fetch 和 await 时如何将用户重定向到不同的页面?
- ios - 如何在 iOS 中仅获取可见日历
- php - 如何使用 AMQPMessage 发送数组 - RabbitMQ
- angular - 如何使用量角器测试日期选择器?
- html - 在小屏幕上将表格更改为两列设计
- aws-lambda - dynamodb 扫描方法返回 null
- angular - Ionic 4 - 使用 localStorage 简单登录
- docker - 如何使用 docker 容器使用 tomcat 或 jenkins
- .htaccess - 使用 gulp 复制 .htaccess(点文件)失败