Я придумал простой пример
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.