2、Demo采用了MVVM框架MVVMLight。由于本人初学MVVM,因此有些代码或者文件的分类不一定是合适的做法,还请注意。
一、列表展示
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<MainViewModel>();
}
public MainViewModel Main => ServiceLocator.Current.GetInstance<MainViewModel>();
public static void Cleanup()
{
}
}
public class MainViewModel: ViewModelBase
{
public MainViewModel()
{
SetSymbolList();
}
private ObservableCollection<SymbolItem> _symbolItems;
public ObservableCollection<SymbolItem> SymbolItems
{
get
{
return _symbolItems;
}
set
{
_symbolItems = value;
RaisePropertyChanged();
}
}
private void SetSymbolList()
{
this.SymbolItems = new ObservableCollection<SymbolItem>();
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的引用
<Application.Resources>
<loc:ViewModelLocator x:Key="Locator"/>
</Application.Resources>
4、MainPage.xaml布局如下:
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ListView ItemsSource="{Binding SymbolItems}" >
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Rectangle Margin="0,3,20,3" Width="3" Height="35" Fill="{ThemeResource SystemControlBackgroundAccentBrush}"/>
<TextBlock Text="{Binding IntVal}" VerticalAlignment="Center"/>
<SymbolIcon Symbol="{Binding Item}" Margin="20,0,20,0"/>
<TextBlock Text="{Binding StringVal}" VerticalAlignment="Center"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
引用的数据上下文DataContext="{Binding Source={StaticResource Locator}, Path=Main}"
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<SymbolItem> SymbolItems
{
get;
private set;
}
public SymbolGroup()
{
this.SymbolItems = new ObservableCollection<SymbolItem>();
}
}
2、ViewModel文件夹下新增GroupMainViewModel类,与MainViewModel类类似。Grouping()方法的功能实现原列表的分组
public class GroupMainViewModel : ViewModelBase
{
public GroupMainViewModel()
{
SetSymbolList();
Grouping();//实现分组
}
private ObservableCollection<SymbolItem> symbolItems;
private ObservableCollection<SymbolGroup> _symbolGroups;
public ObservableCollection<SymbolGroup> SymbolGroups
{
get
{
return _symbolGroups;
}
set
{
_symbolGroups = value;
}
}
private void SetSymbolList()
{
this.symbolItems = new ObservableCollection<SymbolItem>();
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<SymbolGroup>();
List<string> groupList = new List<string>();
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<MainViewModel>();
SimpleIoc.Default.Register<GroupMainViewModel>();
}
public MainViewModel Main => ServiceLocator.Current.GetInstance<MainViewModel>();
public GroupMainViewModel GroupMain => ServiceLocator.Current.GetInstance<GroupMainViewModel>();
public static void Cleanup()
{
}
}
4、MainPage.xaml中定义一个数据集视图CollectionView
<Page.Resources>
<CollectionViewSource x:Name="symbolItems" IsSourceGrouped="True" ItemsPath="SymbolItems" Source="{Binding SymbolGroups}"/>
</Page.Resources>
别忘了把原来的数据上下文修改为GroupMain
DataContext="{Binding Source={StaticResource Locator}, Path=GroupMain}"
5、在原有的ListView模板中新增GroupStyle
<ListView.GroupStyle>
<GroupStyle >
<GroupStyle.HeaderTemplate>
<DataTemplate>
<Button Background="{ThemeResource SystemControlBackgroundAccentBrush}" Content="{Binding Name}"/>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ListView.GroupStyle>
6、运行程序可以看到下面效果
效果图可以看出存在两个问题
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<SymbolGroup>(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
<GridView ItemsSource="{Binding SymbolGroups}">
<GridView.ItemTemplate>
<DataTemplate>
<Border BorderBrush="{ThemeResource SystemControlBackgroundAccentBrush}" BorderThickness="2" Margin="10">
<Grid Height="150" Width="150">
<TextBlock Text="{Binding Name}"/>
</Grid>
</Border>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
将上面的GridView和第二步中的ListView分别放置在ZoomedOutView和ZoomedInView中,代码如下:
<SemanticZoom ViewChangeStarted="SemanticZoom_ViewChangeStarted" >
<SemanticZoom.ZoomedOutView>
<GridView ItemsSource="{Binding SymbolGroups}">
<GridView.ItemTemplate>
<DataTemplate>
<Border BorderBrush="{ThemeResource SystemControlBackgroundAccentBrush}" BorderThickness="2" Margin="10">
<Grid Height="150" Width="150">
<TextBlock Text="{Binding Name}"/>
</Grid>
</Border>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
</SemanticZoom.ZoomedOutView>
<SemanticZoom.ZoomedInView>
<ListView x:Name="listView" ItemsSource="{Binding Source={StaticResource symbolItems}}">
<ListView.GroupStyle>
<GroupStyle >
<GroupStyle.HeaderTemplate>
<DataTemplate>
<Button Background="{ThemeResource SystemControlBackgroundAccentBrush}" Content="{Binding Name}"/>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ListView.GroupStyle>
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Rectangle Margin="0,3,20,3" Width="3" Height="35" Fill="{ThemeResource SystemControlBackgroundAccentBrush}"/>
<TextBlock Text="{Binding IntVal}" VerticalAlignment="Center"/>
<SymbolIcon Symbol="{Binding Item}" Margin="20,0,20,0"/>
<TextBlock Text="{Binding StringVal}" VerticalAlignment="Center"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</SemanticZoom.ZoomedInView>
</SemanticZoom>
运行程序,就可以看到前面图示中的效果
然而,当点击具体某一项时,并不能实现索引,查看MSDN帮助文档找到解决方案
给SemanticZoom控件添加ViewChangeStarted事件
private void SemanticZoom_ViewChangeStarted(object sender, SemanticZoomViewChangedEventArgs e)
{
if (e.IsSourceZoomedInView == false)
{
e.DestinationItem.Item = e.SourceItem.Item;
}
}