Как выбрать и установить фокус строки datagrid программно с помощью шаблона проектирования MVVM в WPF
I have a simple WPF application that has a data grid. What I want is to select and set focus data grid row once I click a button. When the row is selected, I need to change the selected (focused) row using keyboard up/down arrow keys. Most impotent thing is, I want to do this in MVVM design pattern. My application is as below.
My Item.cs class is as below:
public class Item { public string ItemCode { get; set; } public string ItemName { get; set; } public double ItemPrice { get; set; } public Item(string itemCode, string itemName, double itemPrice) { this.ItemCode = itemCode; this.ItemName = itemName; this.ItemPrice = itemPrice; } }
ItemViewModel.cs is as below:
public class ItemsViewModel : INotifyPropertyChanged { private List<Item> _items; public List<Item> ItemsCollection { get { return this._items; } set { _items = value; OnPropertyChanged(nameof(ItemsCollection)); } } public ItemsViewModel() { this.ItemsCollection = new List<Item>(); //Add default items this.ItemsCollection.Add(new Item("I001", "Text Book", 10)); this.ItemsCollection.Add(new Item("I002", "Pencil", 20)); this.ItemsCollection.Add(new Item("I003", "Bag", 15)); } public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(string propertyName) { if (this.PropertyChanged != null) this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } }
MainWindowViewModel.cs as below:
public class MainWindowViewModel : INotifyPropertyChanged { public ICommand SelectRow { get; private set; } public MainWindowViewModel() { this.SelectRow = new RelayCommand(this.SelectGridRow, o => true); } private void SelectGridRow(object param) { //TODO: Code should goes here } public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(string propertyName) { if (this.PropertyChanged != null) this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } }
I have written following RelayCommand.cs to handle the command binding
public class RelayCommand : ICommand { #region Fields /// <summary> /// Encapsulated the execute action /// </summary> private Action<object> execute; /// <summary> /// Encapsulated the representation for the validation of the execute method /// </summary> private Predicate<object> canExecute; #endregion // Fields #region Constructors /// <summary> /// Initializes a new instance of the RelayCommand class /// Creates a new command that can always execute. /// </summary> /// <param name="execute">The execution logic.</param> public RelayCommand(Action<object> execute) : this(execute, DefaultCanExecute) { } /// <summary> /// Initializes a new instance of the RelayCommand class /// Creates a new command. /// </summary> /// <param name="execute">The execution logic.</param> /// <param name="canExecute">The execution status logic.</param> public RelayCommand(Action<object> execute, Predicate<object> canExecute) { if (execute == null) { throw new ArgumentNullException("execute"); } if (canExecute == null) { throw new ArgumentNullException("canExecute"); } this.execute = execute; this.canExecute = canExecute; } #endregion // Constructors #region ICommand Members /// <summary> /// An event to raise when the CanExecute value is changed /// </summary> /// <remarks> /// Any subscription to this event will automatically subscribe to both /// the local OnCanExecuteChanged method AND /// the CommandManager RequerySuggested event /// </remarks> public event EventHandler CanExecuteChanged { add { CommandManager.RequerySuggested += value; this.CanExecuteChangedInternal += value; } remove { CommandManager.RequerySuggested -= value; this.CanExecuteChangedInternal -= value; } } /// <summary> /// An event to allow the CanExecuteChanged event to be raised manually /// </summary> private event EventHandler CanExecuteChangedInternal; /// <summary> /// Defines if command can be executed /// </summary> /// <param name="parameter">the parameter that represents the validation method</param> /// <returns>true if the command can be executed</returns> public bool CanExecute(object parameter) { return this.canExecute != null && this.canExecute(parameter); } /// <summary> /// Execute the encapsulated command /// </summary> /// <param name="parameter">the parameter that represents the execution method</param> public void Execute(object parameter) { this.execute(parameter); } #endregion // ICommand Members /// <summary> /// Raises the can execute changed. /// </summary> public void OnCanExecuteChanged() { EventHandler handler = this.CanExecuteChangedInternal; if (handler != null) { //DispatcherHelper.BeginInvokeOnUIThread(() => handler.Invoke(this, EventArgs.Empty)); handler.Invoke(this, EventArgs.Empty); } } /// <summary> /// Destroys this instance. /// </summary> public void Destroy() { this.canExecute = _ => false; this.execute = _ => { return; }; } /// <summary> /// Defines if command can be executed (default behaviour) /// </summary> /// <param name="parameter">The parameter.</param> /// <returns>Always true</returns> private static bool DefaultCanExecute(object parameter) { return true; } }
I have a ItemView.xaml User control as below:
<UserControl x:Class="DataGrid_FocusRow.ItemView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:DataGrid_FocusRow" mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800"> <Grid> <StackPanel Orientation="Vertical"> <DataGrid x:Name="grdItems" ItemsSource="{Binding ItemsCollection}" AutoGenerateColumns="False" ColumnWidth="*"> <DataGrid.Columns> <DataGridTextColumn Header="Item Code" Binding="{Binding ItemCode}" /> <DataGridTextColumn Header="Item Name" Binding="{Binding ItemName}" /> <DataGridTextColumn Header="Item Price" Binding="{Binding ItemPrice}" /> </DataGrid.Columns> </DataGrid> </StackPanel> </Grid></UserControl>
Мой MainWindow.xaml выглядит следующим образом:
<Window x:Class="DataGrid_FocusRow.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:DataGrid_FocusRow" mc:Ignorable="d" Title="MainWindow" Height="450" Width="800"> <StackPanel> <local:ItemView/> <Button Command="{Binding SelectRow}">Select Row</Button> </StackPanel></Window>
Что я уже пробовал:
I've already tried by setting "SelectedIndex" and "SelectedItem". It selects the row in the data grid. But it does not set focus to the particular row. Because of that I cannot change the selection by UP/DOWN keys in my keyboard.
Gerry Schmitz
"Самое главное" - это то, что он работает ... ваши "паттерны" вторичны.