如何使用Key.Down在ListViews中导航项目

时间:2014-07-04 05:37:09

标签: c# wpf listview

我的情景如下:

<ListView ItemsSource={Binding TestList}>
    <ListView.ItemTemplate>
        <DataTemplate>
            <Expander Header="{Binding Key, BindingMode=OneWay}">
                <Expander.Content>
                    <ListView ItemsSource={Binding Value}>
                        <ListView.ItemContainerStyle>
                            <Style TargetType = {ListViewItem}>
                                <Setter Property = "IsSelected" Value={Binding IsSelected}/>
                            </Style>
                        </ListView.ItemContainerStyle>
                    </ListView>
                </Expander.Content>
            </Expander>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

TestList:

Dictionary<string,ObservableCollection<Test>> TestList;

测试:

class Test : NotificationObject
{
    public string Name { get; set;}

    private bool isSelected;
    public bool IsSelected
    {
        if( value != isSelected )
        {
            isSelected = value;
            RaisePropertyChanged("IsSelected");
        }
    }
}   

内部ListView SelectionMode是Single,我可以使用键盘上下按钮来导航ListView中的项目。有没有办法实现这种情况:当焦点位于ListView的最后一项时,按下键,焦点位于下一个ListView的第一项;当焦点位于ListView的第一项时,按向上键,焦点位于上一个ListView的最后一项。

任何人都可以提供帮助吗?

解决方案

public static List<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject
        {
            List<T> list = new List<T>();
            if (depObj != null)
            {
                for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
                {
                    DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
                    if (child != null && child is T)
                    {
                        list.Add((T)child);
                    }

                    List<T> childItems = FindVisualChildren<T>(child);
                    if (childItems != null && childItems.Count() > 0)
                    {
                        foreach (var item in childItems)
                        {
                            list.Add(item);
                        }
                    }
                }
            }
            return list;
        }

        private void ListView_PreviewKeyDown(object sender, KeyEventArgs e)
        {
            ListView currentListView = sender as ListView;

            List<ListView> listviews = FindVisualChildren<ListView>(ModulesListView);

            int currentListViewIndex = listviews.IndexOf(currentListView);

            // If the press key is Down and the selected item is the last one
            if (Keyboard.IsKeyDown(Key.Down)&& currentListView.SelectedIndex + 1 == currentListView.Items.Count)
            {               
                if (currentListViewIndex + 1 < listviews.Count)
                {
                    // Get next ListView
                    var nextListView = listviews.ElementAt(currentListViewIndex + 1);

                    if (nextListView.Items != null && nextListView.Items.Count > 0)
                    {
                        ListViewItem item = nextListView.ItemContainerGenerator.ContainerFromIndex(0) as ListViewItem;
                        item.Focus();
                        item.IsSelected = true;
                        e.Handled = true;
                    }
                }


            }
            else if (Keyboard.IsKeyDown(Key.Up)&& currentListView.SelectedIndex == 0)
            {
                if (currentListViewIndex > 0)
                {
                    var previousListView = listviews.ElementAt(currentListViewIndex - 1);

                    if (previousListView.Items != null && previousListView.Items.Count > 0)
                    {                                              
                        ListViewItem item = previousListView.ItemContainerGenerator.ContainerFromIndex(previousListView.Items.Count - 1) as ListViewItem;
                        item.Focus();
                        item.IsSelected = true;
                        e.Handled = true;
                    }
                }

            }

        }

2 个答案:

答案 0 :(得分:1)

这个问题已经解决了!我已将问题粘贴到问题下方,我希望它可以帮助其他有相同需求的成员,谢谢!

答案 1 :(得分:0)

在这里,我尝试通过附加属性来解决它

使用附件,您可以实现难以在xaml中实现的可附加行为

这就是你需要的东西

XAML

<Grid xmlns:l="clr-namespace:CSharpWPF">
    <ListView l:NavigationHelper.IsEnabled="True">
        <sys:String>item 1</sys:String>
        <sys:String>item 2</sys:String>
        <sys:String>item 3</sys:String>
        <sys:String>item 4</sys:String>
        <ListView.ItemTemplate>
            <DataTemplate>
                <Expander Header="{Binding}">
                    <Expander.Content>
                        <ListView ItemsSource="{Binding}"
                                  l:NavigationHelper.IsEnabled="True">
                            <ListView.ItemContainerStyle>
                                <Style TargetType="ListViewItem">
                                    <Setter Property="IsSelected"
                                            Value="{Binding IsSelected}" />
                                </Style>
                            </ListView.ItemContainerStyle>
                        </ListView>
                    </Expander.Content>
                </Expander>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
</Grid>

我已将属性l:NavigationHelper.IsEnabled="True"附加到列表视图中以启用行为

NavigationHelper.cs

namespace CSharpWPF
{
    public class NavigationHelper : DependencyObject
    {
        public static bool GetIsEnabled(DependencyObject obj)
        {
            return (bool)obj.GetValue(IsEnabledProperty);
        }

        public static void SetIsEnabled(DependencyObject obj, bool value)
        {
            obj.SetValue(IsEnabledProperty, value);
        }

        // Using a DependencyProperty as the backing store for IsEnabled.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty IsEnabledProperty =
            DependencyProperty.RegisterAttached("IsEnabled", typeof(bool), typeof(NavigationHelper), new PropertyMetadata(false, OnIsEnabledChanged));

        private static void OnIsEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            Selector selector = d as Selector;
            if ((bool)e.NewValue)
                selector.PreviewKeyDown += selector_PreviewKeyDown;
            else
                selector.PreviewKeyDown -= selector_PreviewKeyDown;

        }

        static void selector_PreviewKeyDown(object sender, KeyEventArgs e)
        {
            Selector selector = sender as Selector;
            if (selector.Items.Count == 0)
                return;
            ListViewItem itemToSelect = null;
            if (e.Key == Key.Up && selector.SelectedIndex == 0)
            {
                itemToSelect = selector.ItemContainerGenerator.ContainerFromIndex(selector.Items.Count - 1) as ListViewItem;
            }
            else if (e.Key == Key.Down && selector.SelectedIndex == selector.Items.Count - 1)
            {
                itemToSelect = selector.ItemContainerGenerator.ContainerFromIndex(0) as ListViewItem;
            }
            if (itemToSelect != null)
            {
                selector.SelectedItem = itemToSelect;
                itemToSelect.Focus();
                e.Handled = true;
            }

        }
    }
}

在这个课程中,我创建了一个附加属性,并在启用时附加PreviewKeyDown

并且在事件处理程序中我正在检查是否按下向上键并且选择位于顶部然后选择最后一项并选择它并移动焦点

与最底部的项目相同,键是关闭的,在这种情况下选择第一项。

并且最重要的是通过设置e.Handled = true标记所消耗的事件,因此listview不会进一步处理它。