I'm writing a simple wpf application which contains datagrid. I'm following MVVM design pattern. When the app is initially run, an empty row will be added to the datagrid and I'm focusing the 1st cell of the 1st row by default.
I need following things to be done and I need it to be done with MVVM design pattern.
- When you hit "Enter" key, move focus to next cell
- When the focus is set to the last cell of the grid and user hit the "Enter" key again, new empty row should be added to the grid and focus should be added to the 1st cell of the new row.
I found some similar questions like this(ex : this) but none o f them explains how to do this using MVVM design pattern and my 2nd point above is not included in those posts.
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;
}
}
ItemsViewModel.cs class 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>();
this.ItemsCollection.Add(new Item("", "", 0));
}
#region INotifyPropertyChanged Implementations
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
Following DataGridBehavior.cs class will handle focus of the 1st cell when the app initialization.
public static class DataGridBehavior
{
public static readonly DependencyProperty FocusFirstCellProperty = DependencyProperty.RegisterAttached(
"FocusFirstCell", typeof(Boolean), typeof(DataGridBehavior), new FrameworkPropertyMetadata(false, new PropertyChangedCallback(OnFocusFirstCellChanged)));
public static void SetFocusFirstCell(DataGrid element, Boolean value)
{
element.SetValue(FocusFirstCellProperty, value);
}
public static Boolean GetFocusFirstCell(DataGrid element)
{
return (Boolean)element.GetValue(FocusFirstCellProperty);
}
private static void OnFocusFirstCellChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
DataGrid element = (DataGrid)d;
if (element.IsLoaded)
{
TextBox textBox = FindVisualChild<TextBox>(element);
if (textBox != null)
Keyboard.Focus(textBox);
}
else
{
RoutedEventHandler handler = null;
handler = (ss, ee) =>
{
DataGrid dataGrid = (DataGrid)ss;
TextBox textBox = FindVisualChild<TextBox>((DataGrid)ss);
if (textBox != null)
Keyboard.Focus(textBox);
dataGrid.Loaded -= handler;
};
element.Loaded += handler;
}
}
private static T FindVisualChild<T>(DependencyObject obj) where T : DependencyObject
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(obj, i);
if (child != null && child is T)
return (T)child;
else
{
T childOfChild = FindVisualChild<T>(child);
if (childOfChild != null)
return childOfChild;
}
}
return null;
}
}
Items.xaml user control is as below.
<UserControl x:Class="WpfApp2.Items"
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:WpfApp2"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<StackPanel Orientation="Vertical">
<TextBlock>Search</TextBlock>
<TextBox Text="{Binding Description}" />
<DataGrid x:Name="grdItems" ItemsSource="{Binding ItemsCollection}" AutoGenerateColumns="False" ColumnWidth="*"
local:DataGridBehavior.FocusFirstCell="True">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Item Code">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox x:Name="txtItemCode" Text="{Binding ItemCode}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Item Name">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox x:Name="txtItemName" Text="{Binding ItemName}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Price">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox x:Name="txtItemSellingPrice" Text="{Binding ItemPrice}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</StackPanel>
</Grid>
MainWindow.xaml is as below
<Window x:Class="WpfApp2.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:WpfApp2"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<local:Items/>
</Grid>