Search code examples
wpflistboxnavigationwrappanelarrow-keys

ListBox with WrapPanel - Remove item / arrow key navigation


I have a strange problem regarding my listbox / wrappanel arrow navigation.

My Listbox :

<ListBox x:Name="List" 
     IsSynchronizedWithCurrentItem="True"
     SelectedIndex="{Binding MainIndex, Mode=OneWayToSource}"
     SelectedItem="{Binding CurrentFeed, Mode=TwoWay}"
     ItemsSource="{Binding CurrentFeedList}" 
     ScrollViewer.HorizontalScrollBarVisibility="Disabled" >
<ListBox.ItemsPanel>
    <ItemsPanelTemplate>
        <WrapPanel IsItemsHost="True" MaxWidth="{Binding ActualWidth, ElementName=Panel}" />
    </ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
    <DataTemplate>
        <StackPanel Width="168">
            <Border BorderBrush="White" BorderThickness="0" Margin="0,7,0,0">
                <Image Source="{Binding Image , Converter={StaticResource ByteArraytoImageSource}}" Width="150" Height="213" ></Image>
            </Border>
            <Border BorderBrush="White" BorderThickness="0"  Height="65" HorizontalAlignment="Center" >
                <TextBlock VerticalAlignment="Center" TextWrapping="Wrap"  FontFamily="{StaticResource DefaultFontFamily}" FontSize="15" Text="{Binding Title}" Foreground="White" Cursor="Hand" HorizontalAlignment="Center"  TextAlignment="Center"/>
            </Border>
        </StackPanel>
    </DataTemplate>
</ListBox.ItemTemplate>

The render : Render of Listbox
(source: free.fr)

The render is great No problem ! ;)

As you can see it's a list of wallpaper retrieved from RSS.

I have bind a shortcut to make one item readed and then it's removed from the ObservableCollection :

<UserControl.InputBindings>
    <KeyBinding Key="D" Modifiers="Control" Command="{Binding ReadCmd}" />
</UserControl.InputBindings>

In My View Model :

ObservableCollection<Feed> CurrentFeedList { get; set; }
private Feed _currentFeed;
public Feed CurrentFeed
{
    get { return _currentFeed; }
    set
    {
        _currentFeed = value;
        OnPropertyChanged("CurrentFeed");
    }
}
public ICommand ReadCmd { get; set; }
public int MainIndex { get; set; }

My ReadCmd is a RelayCommand that call "ReadAction" method.

At the beginning I simply remove the item from the ObservableCollection, but I want to type Ctrl+D twice to read 2 item.

So I decided to get the index of the listbox and select back the same index after removing one.

private void ReadAction()
{
    int previousIndex = MainIndex;

    if (previousIndex == CurrentFeedList.Count - 1)
        previousIndex -= 1;

    CurrentFeedList.Remove(CurrentFeed);

    CurrentFeed = CurrentFeedList[previousIndex];
}

It's work as I wanted, but one problem remain and I can't solve it.

When I push Ctrl+D the SelectedItem is remove and the next item become Selected. But then when I use the arrow key navigation to navigate over the list, it jump each time to the first item of the list.

I hope it's enough clear.

Thanks you.


Solution

  • This issue is due to the fact that when you are removing the items using the KeyBinding, ListBox loses the focus and hence pressing the arrow keys after that places focus on first item of the ListBox.

    So in order to make your arrow keys work from the selected item you will also have to set the focus on Selected Item of the Listbox.

    There are couple of ways of doing this, e.g using attached behavior to set the focus on the ListBoxItem. Simplest would be to capture the SelectionChanged event of your ListBox and in the handler set the focus on the currently selected item like below:

        private void MyListBox_OnSelectionChanged(object sender, RoutedEventArgs e)
        {
           var item =  (ListBoxItem)MyListBox.ItemContainerGenerator.ContainerFromItem(MyListBox.SelectedItem);
    
           if(item !=null)
              item.Focus();
        }