1

I wish to have keyboard navigation that is in IconView of Windows Explorer i.e if we move to the End of the width of the Screen selection should move to the next row....

    <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

Ankesh
  • 4,847
  • 4
  • 38
  • 76

2 Answers2

0

In your Generic.xaml define ListView default style as following:

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

Little explanation: your WrapPanel was taking all available size of your ScrollViewer which is virtually infinite. If you want your items to be scrollable in Vertical WrapPanel, you should contraint your height, in Horizontal - width.

Eugene Cheverda
  • 8,760
  • 2
  • 33
  • 18
  • By Doing this my navigation in now Contained... that is If i reach the end of the of the width(by using the right key of keyboard) its stops there i want it to move to the next row... – Ankesh Nov 18 '11 at 12:27
  • 2
    There is the `FocusNavigationDirection` enum which takes part in navigation scenarios, when you press cursor keys on keyboard in case of the WrapPanel you will navigate only within the bounds of one row (left, right) or one column (up, down) and there is no another solution rather than writing your own provider of focusing scenarios. – Eugene Cheverda Nov 18 '11 at 16:21
  • Yes its seems.... so could you give me some direction on writing such navigation providers – Ankesh Nov 19 '11 at 10:13
0

The only way I found out to accomplish that is to manually interpret the pressed key in the PreviewKeyDown event and set the selectedindex. You have to set handled to true, otherwise the listview will interpret the keys too, which leads to wrong key navigation.

Here's an example with two keys:

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

EDIT: 100% pure MVVM way using 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>

ViewModel:

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");
  }
}
SvenG
  • 5,155
  • 2
  • 27
  • 36
  • Thanks for your answer, but i am using MVVM so i dont wish to have code behind.... Can we make a behaviour for your above method...?? – Ankesh Nov 18 '11 at 13:19
  • Okay, I edited my post and added a MVVM way. I used MVVM light toolkit to pass the Keydown event to the VM and bound the SelectedIndex to the ListView. Better way would be to create a CustomControl that behaves correctly. Apart from that having a keydown event and pass it to a ViewModel is absolutely okay with MVVM. – SvenG Nov 18 '11 at 16:23
  • Thanks for the edit....but the porblem i am faceing here is i am wraping vertically so when i press right i wish to go to the adjcent columns object but in your case it doen not work as it on ly moves to previous or next selected items. – Ankesh Nov 19 '11 at 04:08
  • Also taking KeyEventArgs to ViewModel can be avoided by a `Attached Behaviour`i wrote that already using your code... i'll post if you want but still my above comment states the problem – Ankesh Nov 19 '11 at 04:10