Win10开发:ListView实现分组和索引

本篇文章讲解ListView控件的分组和索引的实现


两点说明:

1、Demo中所使用的数据源是SymbolIcon符号,MSDN链接:https://msdn.microsoft.com/zh-cn/library/windows/apps/windows.ui.xaml.controls.symbol.aspx

Win10开发:ListView实现分组和索引_第1张图片


2、Demo采用了MVVM框架MVVMLight。由于本人初学MVVM,因此有些代码或者文件的分类不一定是合适的做法,还请注意。


一、列表展示

在实现分组与索引之前,先做一个简单的列表展示。这里做简要讲解,更详细的请参考文章:Win10开发:构建基于MVVMLight框架的Win10项目

1、Model文件夹下新增SymbolItem类

using Windows.UI.Xaml.Controls;

namespace ListViewDemo.Models
{
    public class SymbolItem
    {
        public Symbol Item { get; set; }
        public int IntVal { get; set; }
        public string StringVal { get; set; }
    }
}

2、ViewModel文件夹下新增ViewModelLocator类和MainViewModel类

public class ViewModelLocator
    {
        public ViewModelLocator()
        {
            ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);

            SimpleIoc.Default.Register();
        }

        public MainViewModel Main => ServiceLocator.Current.GetInstance();

        public static void Cleanup()
        {
        }
    }

public class MainViewModel: ViewModelBase
    {
        public MainViewModel()
        {
            SetSymbolList();
        }

        private ObservableCollection _symbolItems;

        public ObservableCollection SymbolItems
        {
            get
            {
                return _symbolItems;
            }
            set
            {
                _symbolItems = value;
                RaisePropertyChanged();
            }
        }

        private void SetSymbolList()
        {
            this.SymbolItems = new ObservableCollection();

            foreach (Symbol item in Enum.GetValues(typeof(Symbol)))
            {
                //SymbolIcon si = new SymbolIcon((Symbol)Enum.Parse(typeof(Symbol), i.ToString(), true));
                SymbolItem symbolItem = new SymbolItem()
                {
                    Item = item,
                    IntVal=(int)item,
                    StringVal=Enum.GetName(typeof(Symbol),item)
                };
                SymbolItems.Add(symbolItem);
            }
        }
    }

这里对SetSymbolList函数做个简要说明

因为我们要展示的是Symbol,因此我们需要获得系统的所有Symbol枚举Enum.GetValues(typeof(Symbol))

3、在App.xaml新增locator的引用


        
    

4、MainPage.xaml布局如下:

 
        
            
                
                    
                        
                        
                        
                        
                    
                
            
        
    

引用的数据上下文DataContext="{Binding Source={StaticResource Locator}, Path=Main}"


5、运行程序就可以看到效果

Win10开发:ListView实现分组和索引_第2张图片


二、分组实现

1、Model文件夹下新增SymbolGroup类(并不知道这个类是属于Model还是ViewModel合适)

public class SymbolGroup
    {
        private string _name = string.Empty;

        public string Name
        {
            get { return _name; }
            set
            {
                _name = value;
            }
        }

        public ObservableCollection SymbolItems
        {
            get;
            private set;
        }

        public SymbolGroup()
        {
            this.SymbolItems = new ObservableCollection();
        }
    }


2、ViewModel文件夹下新增GroupMainViewModel类,与MainViewModel类类似。Grouping()方法的功能实现原列表的分组

 public class GroupMainViewModel : ViewModelBase
    {
        public GroupMainViewModel()
        {
            SetSymbolList();
            Grouping();//实现分组
        }

        private ObservableCollection symbolItems;

        private ObservableCollection _symbolGroups;

        public ObservableCollection SymbolGroups
        {
            get
            {
                return _symbolGroups;
            }

            set
            {
                _symbolGroups = value;
            }
        }

        private void SetSymbolList()
        {
            this.symbolItems = new ObservableCollection();

            foreach (Symbol item in Enum.GetValues(typeof(Symbol)))
            {
                //SymbolIcon si = new SymbolIcon((Symbol)Enum.Parse(typeof(Symbol), i.ToString(), true));
                SymbolItem symbolItem = new SymbolItem()
                {
                    Item = item,
                    IntVal = (int)item,
                    StringVal = Enum.GetName(typeof(Symbol), item)
                };
                symbolItems.Add(symbolItem);
            }
        }

        private void Grouping()
        {
            SymbolGroups = new ObservableCollection();
            List groupList = new List();
            foreach (SymbolItem item in symbolItems)
            {
                string val = item.StringVal[0].ToString();
                if (groupList.Contains(val))
                {
                    var group = SymbolGroups.First(g => g.Name == val);
                    group.SymbolItems.Add(item);
                }
                else
                {
                    groupList.Add(val);
                    var group = new SymbolGroup()
                    {
                        Name=val
                    };
                    group.SymbolItems.Add(item);
                    SymbolGroups.Add(group);
                }
            }
        }
    }

3、因为新增了一个ViewModel,所以要更新ViewModelLocator类,新增GroupMainViewModel的注册和声明

public class ViewModelLocator
    {
        public ViewModelLocator()
        {
            ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);

            SimpleIoc.Default.Register();
            SimpleIoc.Default.Register();
        }

        public MainViewModel Main => ServiceLocator.Current.GetInstance();
        public GroupMainViewModel GroupMain => ServiceLocator.Current.GetInstance();

        public static void Cleanup()
        {
        }
    }

4、MainPage.xaml中定义一个数据集视图CollectionView


        
别忘了把原来的数据上下文修改为GroupMain

DataContext="{Binding Source={StaticResource Locator}, Path=GroupMain}"

5、在原有的ListView模板中新增GroupStyle

 
                
                    
                        
                            

6、运行程序可以看到下面效果

Win10开发:ListView实现分组和索引_第3张图片


效果图可以看出存在两个问题

a.第一个Item为选中状态

b.分组显示不是从a-z的顺序


下面我们来解决这两个问题

7、对于第一个Item为选中状态的原因暂时未知,解决方案可以在构造函数中取消选中即可

 listView.SelectedItem = null;

8、对于分组不是a-z的问题,修改GroupMainViewModel类的SymbolGroup成员中的Group顺序可以解决

新增排序函数SortSymbolGroup,在分组完成后调用

 private void SortSymbolGroup()
        {
            var copyArray = new SymbolGroup[SymbolGroups.Count];
            SymbolGroups.CopyTo(copyArray, 0);
            SymbolGroups.Clear();

            for (int i = 65; i < 91; i++)//65-90 ASCII A-Z
            {
                try
                {
                    var group = copyArray.First(g => g.Name == ((char)i).ToString());
                    if (group != null)
                    {
                        SymbolGroups.Add(group);
                    }
                }
                catch (Exception)
                {
                    
                }
            }
        }
这里try catch语句的作用是为了捕获泛型类First方法返回值group为空的异常

其实上面的方法用泛型自带的排序方法可以替代:

SymbolGroups = new ObservableCollection(SymbolGroups.OrderBy(g => g.Name).ToList());

到此,分组的实现已经完成,下面讲解索引

三、索引实现

先看效果图.(嗯,长的不是一点点丑...不要在意这些细节2333)

Win10开发:ListView实现分组和索引_第4张图片

实现分组索引需要用到SemanticZoom控件,MSDN中关于SemanticZoom控件的介绍 https://msdn.microsoft.com/query/dev14.query?appId=Dev14IDEF1&l=ZH-CN&k=k(Windows.UI.Xaml.Controls.SemanticZoom);k(VS.XamlEditor);k(TargetFrameworkMoniker-.NETCore,Version%3Dv5.0)&rd=true

SemanticZoom控件一个显示缩小的索引视图(ZoomOutView)和一个显示具体的分组列表(ZoomInView)

第二部分已经讲解了分组列表(ZoomInView)的实现,我们还需要一个GridView实现个缩小的视图,绑定的集合是SymbolGroups


              
                       
                            
                                
                                    
                                
                            
                       
              

将上面的GridView和第二步中的ListView分别放置在ZoomedOutView和ZoomedInView中,代码如下:


            
                
                    
                        
                            
                                
                                    
                                
                            
                        
                    
                
            
            
                
                    
                        
                            
                                
                                    

运行程序,就可以看到前面图示中的效果

然而,当点击具体某一项时,并不能实现索引,查看MSDN帮助文档找到解决方案

给SemanticZoom控件添加ViewChangeStarted事件

  private void SemanticZoom_ViewChangeStarted(object sender, SemanticZoomViewChangedEventArgs e)
        {
            if (e.IsSourceZoomedInView == false)
            {
                e.DestinationItem.Item = e.SourceItem.Item;
            }
        }


这样一个简单的索引导航就实现了


源代码下载:https://github.com/hebecherish/ListViewDemo

你可能感兴趣的:(Windows,10,Win10开发,win10,listview,分组,索引,uwp)