使用mvvm设计模式聚焦wpf datagrid单元

时间:2019-08-05 12:28:58

标签: c# wpf xaml mvvm

我正在使用MVVM设计模式编写简单的WPF应用程序。我的应用程序包含一个具有单个datagrid视图的用户控件。数据网格的每个单元格都是一个文本框列。

我想要设置焦点在数据网格视图中第一个单元格的文本框。

我尝试了this解决方案及其在文本框中的工作。但是它不适用于网格单元格内的文本框。

Item.cs类如下。

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类如下

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用户控件如下。

<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如下。

<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>

2 个答案:

答案 0 :(得分:2)

了解FocusManager的工作方式。 :)

如果您需要一些示例的非正式描述,请查看here

示例:

<TextBox FocusManager.FocusedElement="{Binding RelativeSource={RelativeSource Self}}" />

答案 1 :(得分:2)

您可以实现一种附加行为,即在加载TextBox后在视觉树中找到DataGrid

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

用法:

<DataGrid x:Name="grdItems" ItemsSource="{Binding ItemsCollection}"... 
    local:DataGridBehavior.FocusFirstCell="True">