简体   繁体   English

当 observablecollection ItemSource 更改 WPF 时,通过 MVVM 以编程方式关注行中的特定单元格

[英]Focus on specific cell in row programmatically via MVVM when observablecollection ItemSource changes WPF

I'm trying to set the focus on a specific cell in a row in my DataGrid.我正在尝试将焦点设置在 DataGrid 中一行中的特定单元格上。 The datagrid is bound to an observable collection of X. When I hit a specific button, it causes a new X to be inserted into the observable collection, but there is no focus on the datagrid's side of things and I want to focus the cursor so you can type in the newly created row (a specific cell in the row).数据网格绑定到 X 的可观察集合。当我点击特定按钮时,它会导致将新 X 插入到可观察集合中,但是没有关注数据网格方面的事情,我想关注 cursor 所以您可以输入新创建的行(行中的特定单元格)。 Is this possible going the MVVM route?这可能会走 MVVM 路线吗? My VM has the observable collection, so not too sure where to go from here...我的虚拟机有可观察的集合,所以不太确定 go 从这里到哪里......

I came up with a simple example我想出了一个简单的例子

1) MVVM helper classes (for visibility): 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) Markup 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 and data 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) And the Event handler in code-behind class 4) 以及代码隐藏 class 中的事件处理程序

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
        }));
    }
}

This doesn't break MVVM because Event handler not interacting with data but only check its type.这不会破坏 MVVM,因为事件处理程序不与数据交互而只检查其类型。 Maybe there's a more safe way to check if it's not a NewItemPlaceholder in the e.Row.Item .也许有一种更安全的方法来检查它是否不是NewItemPlaceholder中的e.Row.Item Or you may remove type checking in case CanUserAddRows is set to false.或者,如果CanUserAddRows设置为 false,您可以删除类型检查。

DataGrid在添加行时编辑单元格

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM