在win8上构建按拼音排序的GridView控件

      本人最近因为项目,用C#做了一个可以在win8上使用拼音排序的的GridView控件。其中该控件主要支持以下几个功能:
①支持拼音排序
②支持字母Item和内容Item区分样式
③支持点击字母Item缩放子内容Item
④方便地扩展到各种Model
具体效果如图1所示。接下去对这几个功能的实现进行详细介绍。

图1

1.排序的实现
      win8中没有像wp中的AlphaKeyGroup<T>那样有现成的分类数据结构。所以,我自己写了一个SpellHelper的Class来专门获取一个字符串的首拼字母,如果是符号,则返回“#”。核心代码如下所示。在代码中,可以看到有一个
strChineseFirstPY字符串,它是一个汉字拼音首字母列表, 包含了20902个汉字的U icode 码。获取字符串第一个字符的Unicode与strChineseFirstPY中的字母做映射,从而,能够较快算出第一个拼音。该方法经过测试,处理50000个中文字符串,大概需要47秒左右,在应用中,基本可以满足我们的需求了。
SpellHelper.cs

public static string GetFirstPinyin(string strText,out bool isChar){isChar = true;if (strText == null || strText.Length == 0)return strText;string myStr = string.Empty;char vChar = (strText.ToCharArray())[0];// 若是字母则直接返回if ((vChar >= 'a' && vChar <= 'z') || (vChar >= 'A' && vChar <= 'Z'))myStr = vChar.ToString();else if ((int)vChar >= 19968 && (int)vChar <= 40869){// 对可以查找的汉字计算它的首拼音字母的位置,然后输出myStr = strChineseFirstPY[(int)vChar - 19968].ToString();isChar = false;}else {myStr = "#";}return myStr;}// 获取首字的形状拼音字母

有了以上的辅助类以后,就可以对目标无序集合进行分组了。为了能够达到分组的效果,首先要有一个能将无序模型集合转化成拼音分类排序集合的方法,在本例中,我继承了自GridView的SpellGroupedGridView,,它有名为NotifyDataChanged(ObservableCollection<T>,ModelDataHelper)的一个方法,暴露给用户设置模型数据集合用的。该方法第一个参数就是用户要输入的无序集合,第二个参数是一个模板抽象类,便于扩展,该参数将在后面几节进行介绍。NotifyDataChanged函数的代码如下。
          从代码中可以看出,在 调用NotifyDataChanged方法时,主要完成了两步,第一步调用了buildGroupedList方法生成了一个ObservableCollection<GroupInfoList<object>>类型的数据结构,每个GroupInfoList<T>都有一个Key,即一个字母索引,内部包含了带有该索引的所有模型,也就是说该数据结构最多会有27个GroupInfoList<object>,即a~z和#;第2步,则生成了一个用于匹配到界面的ObservableCollection<object>集合,该集合内的模型与NotifyDataChanged方法输入的的模型集合不一样,它不仅包含了后者的集合,而且对其进行了分组分组排序,而且每组第一个是表示索引的模型。
SpellGroupedGridView.CS

 public void NotifyDataChanged<T>(object data,ModelDataHelper dataHelper){DataHelper = dataHelper;ObservableCollection<T> items = data as ObservableCollection<T>;if (items == null) {this.ItemsSource = null;datasource = null;return;}var groups = buildGroupedList<T>(items);if (groups != null){datasource = buildDataSource<T>(groups);}ItemsSource = datasource;_datas = data;}private ObservableCollection<GroupInfoList<object>> buildGroupedList<T>(ObservableCollection<T> datas){var result = new ObservableCollection<GroupInfoList<object>>();if (datas == null || datas.Count == 0)return result; groupDic = new Dictionary<string, GroupInfoList<object>>();ExpendRecoder = new Dictionary<string, bool>();bool isChar = false;foreach (T person in datas){string keySource = DataHelper.GetKeySource(person);if (string.IsNullOrEmpty(keySource))continue;string key = SpellHelper.GetFirstPinyin(keySource, out isChar).ToUpper();GroupInfoList<object> group = null;if (!groupDic.ContainsKey(key)){group = new GroupInfoList<object>();ExpendRecoder[key] = true;group.Key = key;groupDic[key] = group;}else{group = groupDic[key];}if (isChar)group.Insert(0, person);elsegroup.Add(person);}if (groupDic.ContainsKey("#")){result.Add(groupDic["#"]);}for (int index = 'A'; index >= 'A' && index <= 'Z'; index++){string key = ((char)index).ToString();if (groupDic.ContainsKey(key)){result.Add(groupDic[key]);}}return result;}private ObservableCollection<object> buildDataSource<T>(ObservableCollection<GroupInfoList<object>> datas){if (datas == null || datas.Count == 0)return null;_TitleItems = new ObservableCollection<object>();var ds = new ObservableCollection<object>();foreach (GroupInfoList<object> persons in datas){object titleItem = null;if (DataHelper != null){titleItem = DataHelper.CreateTitleItem(persons.Key as string);}else {titleItem = new TitleItem(persons.Key as string) { Chriendren = persons as GroupInfoList<object> };}_TitleItems.Add(titleItem);ds.Add(titleItem);foreach (T per in persons){ds.Add(per);}}return ds;}

2.索引Item和内容Item的样式

在准备好了分组的数据结构后,就可以进行数据匹配了,即将ObservableCollection<object>集合匹配到SpellGroupedGridView上。因为需要区分索引和内容的样式区分,使用GridView自带的ItemTemplateSelecter和ItemContainerStyleSelecter来对两种类型的数据样式和容器样式进行分别定义,主界面的xaml代码如下所示。为了便于扩展,Selecter都带了来一个DataHelper(见第4节),用于识别item的类型。如果要定义其他的样式,只需要将两个Selecter里面的内容替换即可。
MainPage.xaml.cs

<Pagex:Class="PinyinGridViewTest.MainPage"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:local="using:PinyinGridViewTest"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"xmlns:ctl="using:PinyinGridView.Controls"mc:Ignorable="d"><Page.Resources><Style x:Key="MySourceItemContainerInStyle" TargetType="GridViewItem">。。。</Style><Style x:Key="MySourceItemContainerStyle" TargetType="GridViewItem">。。。</Style><DataTemplate x:Key="header"><Grid Width="394" Height="52" ><Border BorderThickness="2" BorderBrush="Black" Width="34" Height="34" VerticalAlignment="Center" HorizontalAlignment="Left"><TextBlock Text="{Binding Title}" FontSize="24" FontWeight="Bold" Foreground="Black" VerticalAlignment="Center" HorizontalAlignment="Center"/></Border></Grid></DataTemplate><DataTemplate x:Key="MySourcesMatrixTemplate"><Grid Width="394" Height="52" ><StackPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Left"><TextBlock Text="{Binding Title}" FontSize="24" FontWeight="Bold" Foreground="Green" VerticalAlignment="Center" HorizontalAlignment="Center"/><TextBlock Text="{Binding Content}" FontSize="24" FontWeight="Bold" Foreground="Blue" VerticalAlignment="Center" HorizontalAlignment="Center"/></StackPanel></Grid></DataTemplate><ctl:SourceItemEntryDataHelper  x:Key="sourceItemEntryDataHelper"/><ctl:GroupGridViewItemTempleteSelecter x:Key="groupGridViewItemTempleteSelecter"HeaderTemplate="{StaticResource header}" DataHelper="{StaticResource sourceItemEntryDataHelper}"SubTitleTemplete="{StaticResource MySourcesMatrixTemplate}"/><ctl:GroupGridViewItemContainerSelecter x:Key="groupGridViewItemContainerSelecter" DataHelper="{StaticResource sourceItemEntryDataHelper}"HeaderStyle="{StaticResource MySourceItemContainerInStyle}" SubItemStyle="{StaticResource MySourceItemContainerStyle}"></ctl:GroupGridViewItemContainerSelecter></Page.Resources><Grid Background="#f7f7f7"><Grid.RowDefinitions><RowDefinition Height="120" /><RowDefinition Height="*" /></Grid.RowDefinitions><TextBlock FontSize="20" x:Name="notifyText"  Foreground="Red" HorizontalAlignment="Center" VerticalAlignment="Center" /><ctl:SpellGroupedGridView Grid.Row="1"x:Name="itemGridViewIn" Padding="116,0,116,46"HorizontalAlignment="Stretch"VerticalAlignment="Stretch"AutomationProperties.AutomationId="itemGridViewIn"AutomationProperties.Name="Items" SelectionMode="Multiple"TabIndex="1"    ExpendMode="OneItem"  OnTitleItemClick="gv_TitleItemClick"OnSubItemClick="gv_SubItemClick" ItemContainerStyleSelector="{StaticResource groupGridViewItemContainerSelecter}"ItemTemplateSelector="{StaticResource groupGridViewItemTempleteSelecter}"></ctl:SpellGroupedGridView></Grid>
</Page>

3.点击索引Item缩放响应的子Item
            在第1节中,已经建立了分组,索引item能够很方便的得到自己的内容item。所以,在SpellGroupedGridView中对ItemClick事件进行了控制,当点击的item是索引,则响应TitleClick事件,并且当SpellGroupedGridView的ExpendMode设置为OneItem时,每次点击会对其子内容item进行增加删除,表现在ui上就是缩放;当点击的内容item则响应SubItemClick事件。其代码如下所示。

SpellGroupedGridView.CS

       private  void gv_ItemClick(object sender, ItemClickEventArgs e){if (e.ClickedItem is TitleItem || (DataHelper != null && DataHelper.IsTitleItem(e.ClickedItem))){var title = e.ClickedItem as TitleItem;if (ExpendMode.OneItem == ExpendMode){DoToggleExpend(e.ClickedItem);}if (OnTitleItemClick!=null)OnTitleItemClick(sender,e);}else {if(OnSubItemClick!=null)OnSubItemClick(sender,e);}}public void DoToggleExpend(object title) {string key = DataHelper.GetTitleItemAlpha(title);var chrenden = groupDic[key];if (chrenden == null)return;if (ExpendRecoder[key]){foreach (object p in chrenden){datasource.Remove(p);}}else{int index = datasource.IndexOf(title) + 1;for (int i = groupDic[key].Count - 1; i >= 0; i--){datasource.Insert(index, chrenden[i]);}}ExpendRecoder[key] = !ExpendRecoder[key];}public void DeleteItem(object oldItem) {string keySource = DataHelper.GetKeySource(oldItem);if (string.IsNullOrEmpty(keySource))return;bool isChar = false;string key = SpellHelper.GetFirstPinyin(keySource, out isChar).ToUpper();int index = datasource.IndexOf(oldItem);if (index < 0)return;datasource.Remove(oldItem);groupDic[key].Remove(oldItem);if (DataHelper.IsTitleItem(datasource[index - 1]) && groupDic[key].Count==0){groupDic.Remove(key);ExpendRecoder.Remove(key);TitleItems.Remove(datasource[index - 1]);datasource.RemoveAt(index-1);}}

4.关于使用及扩展
           为了便于扩展,能够适应各种不同的模型集合,把 SpellGroupedGridView所需要的模型特性抽象到了ModelDataHelper的抽象类中,其数据结构如下所示,当使用SpellGroupedGridView时,要使用自己的moedl集合,只要继承ModelDatahelper就可以轻松完成扩展(本例中使用的模型为SourceItemEntry,SourceItemEntryHelper继承了ModelDataHelper),对于视图,只要对两个Selecter的内容进行设置就可以方便扩展(见第2节)。对于数据源的设置,NotifyDataChanged代替了直接对ItemSource的直接设置。
ModelDataHelper.cs

<span style="color:#000000;"> public class ModelDataHelper{public ModelDataHelper(){}public virtual bool IsTitleItem(object item) {return false;}public virtual string GetKeySource(object item) { return null; }public virtual object CreateTitleItem(string alpha) { return null; }public virtual string GetTitleItemAlpha(object titleItem) {return "";}}</span>

SourceItemEntry.cs

<span style="color:#000000;">public class SourceItemEntry{public SourceItemEntry(){ }public SourceItemEntry(string id, string title, string content){ID = id;Title = title;Content = content;}public string Title { set; get; }public string Content { set; get; }public string ID { set; get; }}</span>

SourceItemEntryDataHelper.cs

<span style="color:#000000;">  class SourceItemEntryDataHelper : ModelDataHelper{public override string GetKeySource(object item){SourceItemEntry entry = item as SourceItemEntry;if (entry == null)return null;return entry.Title;}public override string GetTitleItemAlpha(object titleItem){SourceItemEntry entry = titleItem as SourceItemEntry;if (entry == null)return "";return entry.Title;}public override object CreateTitleItem(string alpha){return new SourceItemEntry() { Title = alpha, ID = "-4" };}public override bool IsTitleItem(object item){SourceItemEntry entry = item as SourceItemEntry;if (entry == null)return false;if ("-4".Equals(entry.ID))return true;return false;}}</span>

这样,就是想了想要的功能。第一次写博客,希望多提宝贵意见。

在win8上构建按拼音排序的GridView控件相关推荐

  1. android程序设计排序方法,Android编程之可以实现拖动排序的listview控件

    DragSortListView是一个可以实现拖动排序的listview控件,是我看到的交互较为复杂的开源代码中不管是代码质量还是流畅性都最好的. DragSortListView 简称DSLV,继承 ...

  2. 扩展GridView控件(2) - 复合排序和排序状态提示

    GridView既强大又好用.为了让它更强大.更好用,我们来写一个继承自GridView的控件. [索引页] [源码下载] 扩展GridView控件(2) - 复合排序和排序状态提示 作者:webab ...

  3. Android上一种用于选择颜色的控件(颜色选择器)

    目录 引言 核心代码 控件整体代码 demo 引言 最近在做一个项目时其中有一个需求–自定义灯光颜色.要求通过手机端控制灯光颜色,手机端预设五种颜色及用户可自定义颜色.在百度上搜索找到一个开源的色环控 ...

  4. android中文首字母排序,Android上汉字按拼音排序如何实现?

    具体的代码在 packages\providers\contactsprovider\src\com\android\providers\contacts\ContactL ocaleUtils.ja ...

  5. 一个Repeater排序用的控件

    在Repeater上的字段排序需要自己来实现,下面这个控件用来为Repeater上的列提供排序功能,可以添加任意多个列,每次只针对一个列进行排序. .aspx页代码   如果使用网站形式,那么将.cs ...

  6. 实现拖拽上传文件的一款小控件——dropzone

    由于专注所以专业.非常多小巧的东西乍一看非常不起眼,却在特定的领域表现不俗,就是由于集中了热情. dropzone就是这样一款小控件,实现拖拽上传.它不依赖于其他像jquery等JS库.并且支持多方面 ...

  7. 上接扩展GridView控件(10) - 自定义分页样式

    5.重写OnRowCreated以实现自定义分页样式 /// <summary>                  /// OnRowCreated                  // ...

  8. QT上设置背景图不影响子控件以及按钮控件的透明化

    QT中设置背景图不影响子控件 QT中设置背景图的方法不少,我这里是直接使用stylesheet来设置背景图.如果直接在UI界面进行如下所示设置 border-image: url(:/resource ...

  9. 解决了界面上菜单项跑到其它AE控件后面的问题(java)

    菜单项竟然跑到后面去了,搞了好多天,终于解决了 只要把这句代码放到main()的最前面就可以了 JPopupMenu.setDefaultLightWeightPopupEnabled(false); ...

最新文章

  1. ExecutorService 的理解与使用
  2. ICLR2020:40篇计算机视觉github开源论文合集
  3. C#串口通信—传输文件测试
  4. Condition接口详解
  5. eclipse中git插件配置 编辑
  6. JMETER从JSON响应中提取数据
  7. 2020webpack的面试题(webpack)
  8. 认识与防御XSS攻击
  9. c++ primer plus 内存模型和名称空间
  10. 淘宝用户行为分析项目报告
  11. python拦截广告弹窗_Python Selenium关闭弹出广告。风
  12. 心智与认知(1): 反馈循环(Feedback loop)
  13. matlab进行复数计算
  14. 生信:1:vcf格式文件解读
  15. Excel的高级筛选——数据匹配
  16. MongoDB学习(三)
  17. MC 1.19.3+forge+暮色
  18. swift class的虚函数表、扩展、@objc修饰、虚函数的派发方式研究
  19. java继承a mya new c,“内部类” 大总结(Java),内部类总结java
  20. 记一次低级错误:feign.FeignException: status 404 reading XXXClient#XXMethod(Long)

热门文章

  1. hdu1847(SG函数)
  2. 运动蓝牙耳机什么牌子的好,跑步运动耳机推荐
  3. jsp用idea打war包,部署阿里云ecs
  4. Python-调试(各种方式)
  5. cocos2D安装及SDK,Ant,JDK的环境配置
  6. 2021新版pycharm的下载教程
  7. python类静态变量
  8. ios 自己服务器 苹果支付_thinkphp整合系列之苹果AppStore内购付款的服务器端php验证...
  9. 学习笔记之-----H5API
  10. 华为p10黑屏问题解决