【问题标题】:WPF DataGridView keyboard focus from command/viewmodel来自命令/视图模型的 WPF DataGridView 键盘焦点
【发布时间】:2015-05-18 17:30:15
【问题描述】:

网上到处都是类似的问题,我也搜索了 SO(最接近的:[1]、[2]、[3])。

到目前为止,我无法相信这个问题是如此丑陋/不平凡。

我有一个 DataGridView。我有其他控件(在这种情况下:在 DataGridView 的列标题中)。我想支持一个快速的“跳转到网格”命令。此命令需要将键盘焦点设置为网格,以便用户可以使用箭头键在行之间导航。

在下面在家玩的简单测试用例。在数据网格中选择一个元素非常容易,但似乎没有办法让它成为键盘焦点。除了在代码隐藏操作中,即使那样,您似乎也必须跳过箍(请参阅 [2],杂耍让单元格容器设置键盘焦点,因为 .. 网格和行似乎并不适用于所有我知道)。

看起来很简单?

一些简单的模型:

public class Item
{
    public int DayOfMonth { get; set; }
    public string Weekday { get; set; }
}

匹配琐碎的视图模型(假设您有一个 RelayCommand 实现,请参阅 JumpToGrid for the ~meat~):

public class MainViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private IList<Item> m_items;
    public IList<Item> SampleItems
    {
        get { return m_items; }
        set { SetField(ref m_items, value, () => SampleItems); }
    }

    private Item m_currentItem;
    public Item CurrentItem
    {
        get { return m_currentItem; }
        set { SetField(ref m_currentItem, value, () => CurrentItem); }
    }

    public ICommand JumpToGridCommand { get; private set; }

    public MainViewModel()
    {
        JumpToGridCommand = new RelayCommand(p => JumpToGrid());

        var items = new List<Item>();
        var today = DateTime.Now;
        for (int i = 1; i <= DateTime.DaysInMonth(today.Year, today.Month); i++ )
        {
            items.Add(new Item { DayOfMonth = i, Weekday = new DateTime(today.Year, today.Month, i).DayOfWeek.ToString() });
        }
        SampleItems = items;
    }

    private void JumpToGrid()
    {
        // I can change the selection just fine
        CurrentItem = SampleItems[0];

        // But the keyboard focus is broken, up/down doesn't work as expected
    }

    protected bool SetField<T>(ref T field, T value, Expression<Func<T>> selectorExpression)
    {
        if (selectorExpression == null) throw new ArgumentNullException("selectorExpression");
        MemberExpression body = selectorExpression.Body as MemberExpression;
        if (body == null) throw new ArgumentException("The body must be a member expression");
        var fieldName = body.Member.Name;

        if (EqualityComparer<T>.Default.Equals(field, value)) return false;
        field = value;
        OnPropertyChanged(fieldName);
        return true;
    }

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

简单视图(代码隐藏为空,加载事件中的“DataContext = new MainViewModel()”除外): <Window x:Class="DataGridKeyboardFocus.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:DataGridKeyboardFocus" Loaded="Window_Loaded" Title="MainWindow" Height="350" Width="525"> <Grid> <DataGrid ColumnWidth="*" AutoGenerateColumns="False" Margin="1" ItemsSource="{Binding SampleItems}" SelectedItem="{Binding CurrentItem, Mode=TwoWay}" SelectionUnit="FullRow" IsReadOnly="True"> <DataGrid.Columns> <DataGridTextColumn Binding="{Binding DayOfMonth}"> <DataGridTextColumn.HeaderTemplate> <DataTemplate> <StackPanel Orientation="Vertical"> <TextBlock Text="Some label" /> <TextBox> <TextBox.InputBindings> <KeyBinding Modifiers="Control" Key="Tab" Command="{Binding DataContext.JumpToGridCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:MainWindow}}}" /> </TextBox.InputBindings> </TextBox> </StackPanel> </DataTemplate> </DataGridTextColumn.HeaderTemplate> </DataGridTextColumn> <DataGridTextColumn Binding="{Binding Weekday}"> <DataGridTextColumn.HeaderTemplate> <DataTemplate> <StackPanel Orientation="Vertical"> <TextBlock Text="Another label" /> <TextBox> <TextBox.InputBindings> <KeyBinding Modifiers="Control" Key="Tab" Command="{Binding DataContext.JumpToGridCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:MainWindow}}}" /> </TextBox.InputBindings> </TextBox> </StackPanel> </DataTemplate> </DataGridTextColumn.HeaderTemplate> </DataGridTextColumn> </DataGrid.Columns> </DataGrid> </Grid> </Window>

1:Keyboard focus vs logical focus in WPF

2:Keyboard focus to DataGrid

3:WPF: Can't control keyboard focus

【问题讨论】:

    标签: .net wpf datagridview wpfdatagrid


    【解决方案1】:

    我为网格添加了选择更改事件并尝试选择一个单元格。参考以下代码。

     <DataGrid
                ColumnWidth="*" 
                AutoGenerateColumns="False"
                Margin="1"
                ItemsSource="{Binding SampleItems}" 
                SelectedItem="{Binding CurrentItem, Mode=TwoWay}"
                SelectionUnit="FullRow"
                IsReadOnly="True"
            SelectionChanged="DataGrid_SelectionChanged">
            <DataGrid.Columns>
                <DataGridTextColumn Binding="{Binding DayOfMonth}">
                    <DataGridTextColumn.HeaderTemplate>
                        <DataTemplate>
                            <StackPanel Orientation="Vertical">
                                <TextBlock Text="Some label" />
                                <TextBox>
                                    <TextBox.InputBindings>
                                        <KeyBinding Modifiers="Control" Key="T" Command="{Binding DataContext.JumpToGridCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" />
                                    </TextBox.InputBindings>
                                </TextBox>
                            </StackPanel>
                        </DataTemplate>
                    </DataGridTextColumn.HeaderTemplate>
                </DataGridTextColumn>
                <DataGridTextColumn Binding="{Binding Weekday}">
                    <DataGridTextColumn.HeaderTemplate>
                        <DataTemplate>
                            <StackPanel Orientation="Vertical">
                                <TextBlock Text="Another label" />
                                <TextBox>
                                    <TextBox.InputBindings>
                                        <KeyBinding Modifiers="Control" Key="T" Command="{Binding DataContext.JumpToGridCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" />
                                    </TextBox.InputBindings>
                                </TextBox>
                            </StackPanel>
                        </DataTemplate>
                    </DataGridTextColumn.HeaderTemplate>
                </DataGridTextColumn>
            </DataGrid.Columns>
        </DataGrid>
    

    代码隐藏。

    private void DataGrid_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            DataGrid dg = sender as DataGrid;
            SelectRowByIndex(dg, dg.SelectedIndex);
        }
        public static DataGridCell GetCell(DataGrid dataGrid, DataGridRow rowContainer, int column)
        {
            if (rowContainer != null)
            {
                DataGridCellsPresenter presenter = FindVisualChild<DataGridCellsPresenter>(rowContainer);
                if (presenter == null)
                {
                    /* if the row has been virtualized away, call its ApplyTemplate() method
                     * to build its visual tree in order for the DataGridCellsPresenter
                     * and the DataGridCells to be created */
                    rowContainer.ApplyTemplate();
                    presenter = FindVisualChild<DataGridCellsPresenter>(rowContainer);
                }
                if (presenter != null)
                {
                    DataGridCell cell = presenter.ItemContainerGenerator.ContainerFromIndex(column) as DataGridCell;
                    if (cell == null)
                    {
                        /* bring the column into view
                         * in case it has been virtualized away */
                        dataGrid.ScrollIntoView(rowContainer, dataGrid.Columns[column]);
                        cell = presenter.ItemContainerGenerator.ContainerFromIndex(column) as DataGridCell;
                    }
                    return cell;
                }
            }
            return null;
        }
        public 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;
        }
        public static void SelectRowByIndex(DataGrid dataGrid, int rowIndex)
        {
            DataGridRow row = dataGrid.ItemContainerGenerator.ContainerFromIndex(rowIndex) as DataGridRow;
            if (row != null)
            {
                DataGridCell cell = GetCell(dataGrid, row, 0);
                if (cell != null)
                    cell.Focus();
            }
        }
    

    参考链接。 http://social.technet.microsoft.com/wiki/contents/articles/21202.wpf-programmatically-selecting-and-focusing-a-row-or-cell-in-a-datagrid.aspx

    【讨论】:

    • 这 ~ 或多或少是我在 [2] 中发现的,我没能早点开始工作,但事实证明这是我这边/在实际应用程序中的一个问题。您通过修复我的测试用例向我展示了这种方法效果很好,这是我 .. 没有做的事情。为我感到羞耻。谢谢你。
    猜你喜欢
    • 1970-01-01
    • 2012-04-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-01
    相关资源
    最近更新 更多