LIstView 键盘导航类似于 WPF 中的 Windows 资源管理器

Posted

技术标签:

【中文标题】LIstView 键盘导航类似于 WPF 中的 Windows 资源管理器【英文标题】:LIstView keyborad navigation like Windows Explorer in WPF 【发布时间】:2012-01-01 02:29:33 【问题描述】:

我希望在 Windows 资源管理器的 IconView 中使用键盘导航,即如果我们移动到屏幕选择宽度的末尾应该移动到下一行......

    <ListView Name="lv"
              Grid.Row="1"
              Width="Auto"
              Height="Auto"
              IsTextSearchEnabled="True"
              ItemsSource="Binding Path=Persons"
              KeyboardNavigation.DirectionalNavigation="Continue"
              SelectedItem="Binding Path=SelectedPerson"
              SelectionMode="Single"
              View="StaticResource ResourceKey=plainView">
        <ListView.Resources>
            <Style TargetType="x:Type ListViewItem" BasedOn="StaticResource x:Type ListViewItem">
                <Style.Triggers>
                    <DataTrigger Binding="Binding Path=IsSelected" Value="True">
                        <Setter Property="IsEnabled"  Value="False"></Setter>    
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </ListView.Resources>
    </ListView>

Here is the Code for what i am trying

【问题讨论】:

我也试过KeyboardNavigation.DirectionalNavigation="Cycle" 【参考方案1】:

在您的 Generic.xaml 中定义 ListView 默认样式如下:

<Style TargetType="x:Type ListView">
        <Setter Property="SnapsToDevicePixels" Value="true" />        
        <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Visible" />
        <Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Visible" />
        <Setter Property="ScrollViewer.CanContentScroll" Value="True" />
        <Setter Property="HorizontalContentAlignment" Value="Stretch" />
        <Setter Property="VerticalContentAlignment" Value="Center" />
        <Setter Property="FontFamily" Value="Trebuchet MS" />
        <Setter Property="FontSize" Value="12" />
        <Setter Property="BorderBrush" Value="DynamicResource ControlBorderBrush" />
        <Setter Property="BorderThickness" Value="1" />
        <Setter Property="Padding" Value="1" />
        <Setter Property="IsTabStop" Value="False" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="x:Type ListView">
                    <Grid>
                        <Border x:Name="Border"
                                Background="DynamicResource ControlBackgroundBrush"
                                BorderBrush="TemplateBinding BorderBrush"
                                BorderThickness="TemplateBinding BorderThickness"
                                CornerRadius="1">
                            <ScrollViewer Margin="TemplateBinding Padding" IsTabStop="False">                                
                                    <ItemsPresenter/>
                            </ScrollViewer>
                        </Border>
                        <Border x:Name="DisabledVisualElement"
                                Background="#A5FFFFFF"
                                BorderBrush="#66FFFFFF"
                                BorderThickness="1"
                                IsHitTestVisible="false"
                                Opacity="0" />
                    </Grid>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsEnabled" Value="false">
                            <Setter TargetName="DisabledVisualElement" Property="Opacity" Value="1" />
                        </Trigger>
                        <Trigger Property="IsGrouping" Value="true">
                            <Setter Property="ScrollViewer.CanContentScroll" Value="false" />
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
        <Setter Property="ItemsPanel">
            <Setter.Value >
                <ItemsPanelTemplate>
                    <WrapPanel Height="Binding ActualHeight,
                                                            RelativeSource=RelativeSource AncestorType=Border"
                                           MinWidth="Binding (ListView.View).MinWidth,
                                                              RelativeSource=RelativeSource Mode=FindAncestor,
                                                                                             AncestorType=x:Type ListView"
                                           Focusable="False"
                                           IsItemsHost="True"
                               KeyboardNavigation.DirectionalNavigation="Contained"
                                           ItemWidth="Binding (ListView.View).ItemWidth,
                                                               RelativeSource=RelativeSource Mode=FindAncestor,
                                                                                              AncestorType=x:Type ListView"                                           
                                           Orientation="Vertical" />
                </ItemsPanelTemplate>
            </Setter.Value>
        </Setter>
    </Style>

一点解释:您的WrapPanel 占用了您的ScrollViewer 的所有可用大小,这实际上是无限的。如果您希望您的项目在 Vertical WrapPanel 中可滚动,您应该在水平 - 宽度中限制您的高度。

【讨论】:

通过这样做我的导航现在包含...也就是说,如果我到达宽度的末端(通过使用键盘的右键)它会停在那里,我希望它移动到下一行... FocusNavigationDirection 枚举参与导航场景,当您在 WrapPanel 的情况下按下键盘上的光标键时,您将仅在一行(左,右)的范围内导航或一列(向上,向下),没有其他解决方案,而不是编写自己的聚焦场景提供程序。 是的,看起来......所以你能给我一些关于编写这样的导航提供程序的指导【参考方案2】:

我发现完成此操作的唯一方法是手动解释按下的键 PreviewKeyDown 事件并设置 selectedindex。 必须将handled设置为true,否则listview也会解释按键,导致按键导航错误。

这是一个有两个键的示例:

    private void listView_PreviewKeyDown(object sender, KeyEventArgs e)
    
      if (listView.SelectedIndex >= 0)
      
        if (e.Key == Key.Right)
        
          listView.SelectedIndex++;
        
        if (e.Key == Key.Left)
        
          listView.SelectedIndex--;
        
      e.Handled = true;
      
   

编辑: 100% 纯 MVVM 方式使用 MVVMLight Toolkit

XAML:

    xmlns:mvvmLight="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Extras.WPF4"
    xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
    >
<Grid>
<ListView SelectedIndex="Binding SelectedIndex" ItemsSource="Binding Items" x:Name="listView">
  <i:Interaction.Triggers>
    <i:EventTrigger EventName="PreviewKeyDown">
      <mvvmLight:EventToCommand PassEventArgsToCommand="True" Command="Binding PreviewKeyDownCommand"></mvvmLight:EventToCommand>
    </i:EventTrigger>
  </i:Interaction.Triggers>

视图模型:

public ICommand PreviewKeyDownCommand

  get
  
    return new RelayCommand<Object>(x => this.PreviewKeyDown(x as KeyEventArgs));
  


private void PreviewKeyDown(KeyEventArgs e)

  if (SelectedIndex >= 0)
  
    if (e.Key == Key.Right)
    
      SelectedIndex++;
    
    if (e.Key == Key.Left)
    
      SelectedIndex--;
    
  
  e.Handled = true;


private int _selectedIndex;

public int SelectedIndex

  get  return _selectedIndex; 
  set
  
    _selectedIndex = value;
    NotifyPropertyChanged("SelectedIndex");
  

【讨论】:

感谢您的回答,但我使用的是 MVVM,所以我不希望后面有代码....我们可以为您的上述方法做出行为吗...?? 好的,我编辑了我的帖子并添加了 MVVM 方式。我使用 MVVM light toolkit 将 Keydown 事件传递给 VM,并将 SelectedIndex 绑定到 ListView。更好的方法是创建一个行为正确的 CustomControl。除了拥有一个 keydown 事件并将其传递给 ViewModel 之外,MVVM 绝对没问题。 感谢您的编辑....但是我在这里面临的问题是我正在垂直包装,所以当我按右时我希望转到相邻列对象但在你的情况下它不起作用因为它只会移动到上一个或下一个选定的项目。 Attached Behaviouri 写道,已经在使用您的代码,也可以避免将 KeyEventArgs 带到 ViewModel...

以上是关于LIstView 键盘导航类似于 WPF 中的 Windows 资源管理器的主要内容,如果未能解决你的问题,请参考以下文章

ListView WPF上的页脚

具有多行的 WPF 水平 ListView

ListView下面的WPF按钮 - 当ListView太大时消失

WPF 键盘导航附加属性解决TreeView的Tab导航焦点问题

WPF treeview:如何在资源管理器中实现键盘导航?

如何将键盘导航焦点传递给 WPF 中生成的 xaml?以及如何退货?