2

I'm writing simple WPF application using MVVM design pattern. My application contains a user control with single datagrid view. Each of cell of the data grid is a textbox column.

What I want is to set focus the textbox of the 1st cell in the data grid view.

I tried this solution and its working for a textbox. But its not working for the textbox which is inside grid cell.

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

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged(string propertyName)
    {
        if (this.PropertyChanged != null)
            this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}

Item.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">
        <DataGrid x:Name="grdItems" ItemsSource="{Binding ItemsCollection}" AutoGenerateColumns="False" ColumnWidth="*">
            <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>

Prabodha
  • 520
  • 1
  • 6
  • 19

2 Answers2

2

Learn how FocusManager works. :)

If you need a less formal description with some examples then look here

Example :

<TextBox FocusManager.FocusedElement="{Binding RelativeSource={RelativeSource Self}}" />
Zer0
  • 1,146
  • 6
  • 15
2

You could implement an attached behaviour that finds the TextBox in the visual tree once the DataGrid has been loaded:

public static class DataGridBehavior
{
    public static readonly DependencyProperty FocusFirstCellProperty = DependencyProperty.RegisterAttached(
        "FocusFirstCell", typeof(Boolean), typeof(DataGridBehavior), new FrameworkPropertyMetadata(false, new PropertyChangedCallback(OnChanged)));

    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 OnChanged(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;
    }
}

Usage:

<DataGrid x:Name="grdItems" ItemsSource="{Binding ItemsCollection}"... 
    local:DataGridBehavior.FocusFirstCell="True">
mm8
  • 163,881
  • 10
  • 57
  • 88