My understanding is that methods implementing the functionality of a user control should reside in the code behind the user control's xaml and not be defined in the ViewModel. The ViewModel should be decoupled from, and know nothing about, the View. My suggestion is to add the 'Select Row' button to the UserControl
and subscribe to its button click event in the code behind. Set the DataContext
in the xaml, binding the SelectedItem
property of the DataGrid
to a SelectedItem
property defined in the ViewModel like this.
<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">
<UserControl.DataContext>
<local:ItemsViewModel/>
</UserControl.DataContext>
<Grid>
<StackPanel Orientation="Vertical">
<DataGrid x:Name="grdItems" ItemsSource="{Binding ItemsCollection}" SelectedItem="{Binding Path=SelectedItem, Mode=TwoWay}" 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>
<Button Click="Button_Click">Select Row</Button>
</StackPanel>
</Grid>
</UserControl>
The code behind should contain the following partial class definition that adds the functionality you require when the button is clicked.
public partial class ItemView : UserControl
{
public ItemView()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
if (grdItems.Items.Count == 0) return;
object item = grdItems.Items[0];
grdItems.SelectedItem= item;
grdItems.ScrollIntoView(item);
var row = (DataGridRow)grdItems.ItemContainerGenerator.ContainerFromIndex(grdItems.SelectedIndex);
row.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
}
}
If you add the
SelectedItem
property to the ViewModel it will be bound to the
SelectedItem
in the
DataGrid
.
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace DataGrid_FocusRow
{
public class ItemsViewModel : INotifyPropertyChanged
{
private ObservableCollection<Item> _items;
public ObservableCollection<Item> ItemsCollection
{
get { return _items; }
set
{
_items = value;
OnPropertyChanged();
}
}
private Item selectedItem;
public Item SelectedItem
{
get
{
return selectedItem;
}
set
{
selectedItem = value;
OnPropertyChanged();
}
}
public ItemsViewModel()
{
this.ItemsCollection = new ObservableCollection<Item>
{
new Item("I001", "Text Book", 10),
new Item("I002", "Pencil", 20),
new Item("I003", "Bag", 15)
};
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
I should add that the code for setting the focus on a DataGrid Row
is not original, I've saved it as a snippet in the past but have failed to find the original source.