本篇文章讲解ListView控件的分组和索引的实现
两点说明:
1、Demo中所使用的数据源是SymbolIcon符号,MSDN链接:https://msdn.microsoft.com/zh-cn/library/windows/apps/windows.ui.xaml.controls.symbol.aspx
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);
}
}
}
因为我们要展示的是Symbol,因此我们需要获得系统的所有Symbol枚举Enum.GetValues(typeof(Symbol))
3、在App.xaml新增locator的引用
5、运行程序就可以看到效果
二、分组实现
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();
}
}
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}"
效果图可以看出存在两个问题
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)
实现分组索引需要用到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
然而,当点击具体某一项时,并不能实现索引,查看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