使用 Windows Phone App 时,经常会遇到的问题
“列表这么长,Item 这么多,为什么我不能直接拉到指定的 Item?”
本文章旨为解决此问题,让各位开发者都能轻松替自己的 ListBox 加值,成为 “ListBox、改”
本文从替内建控件加值的角度出发,希望读者能够从中了解如何自行撰写一个 CustomControl (自订控件)
使用 Windows Phone App 时,经常会遇到的问题
“列表这么长,Item 这么多,为什么我不能直接拉到指定的 Item?”
本文章旨为解决此问题,让各位开发者都能轻松替自己的 ListBox 加值,成为 “ListBox、改”
本文从替内建控件加值的角度出发,希望读者能够从中了解如何自行撰写一个 CustomControl (自订控件)
预计达成的目标
- 撰写一个新的控件 ListBoxWithScrollBar,提供使用者快速切换到想看的地方
- 避免现有程序更动幅度过大,新控件必须相容旧有的 ListBox
实践步骤
- 将 ListBoxWithScrollBar 继承于 ListBox
- 于 ListBoxWithScrollBar 的外观配置中,加入 Slider,并预计将它用来作为 ScrollBar
- 调整上述 Slider 的外观配置,让它长得像 ScrollBar 该有的样子
- 让 ScrollBar 能做出该有的动作
开始动手吧
于项目中新增一个类 (Class),名为 ListBoxWithScrollBar
为了能让自己撰写用来强化 ListBox 的新控件“ListBoxWithScrollBar”能够相容原有的 ListBox,故直接继承它
public class ListBoxWithScrollBar : ListBox
为了相容于 ListBox,我决定参考 (偷) ListBox 的 Template
随便拉一个 ListBox,对它按右键 → 编辑范本 → 编辑副本,跳出建立 Style 资源窗口后,名称不重要,定义于请选此文档
在该 XAML 档中即可发现如下面的 Style
<style targettype="ListBox" x:key="ListBoxStyle1">
</style>
很神奇吧,这就是原生控件 ListBox 的 Template,换句话说,这就是 ListBox 的外观呈现
ListBox 可以滚动的原因在于里面有一个名为 "ScrollViewer" 的 ScrollViewer 控件
ListBox 可以产生一个个 Item 的原因在于里面有 ItemsPresenter 控件
这个 Template 需要做一些修改,才可套用在 ListBoxWithScrollBar 上
首先将整个 Template 复制下来,贴到 App.xaml 中,并注意需置于 Application.Resources 标签内,并小心不要动到原有的项目
之所以要贴到 App.xaml 中,是因为 CustomControl 的外观都需要置于一个全域的 Application.Resources 中
接下来要将这个 Template 的 TargetType 改为 ListBoxWithScrollBar
<style targettype="CustomControl:ListBoxWithScrollBar">
</style>
切回到 ListBoxWithScrollBar.cs,在建构子中加入 DefaultStyleKey = typeof(ListBoxWithScrollBar); ,如此便能使自订控件套用刚刚写好的 Template
public ListBoxWithScrollBar()
{
DefaultStyleKey = typeof(ListBoxWithScrollBar);
}
在 ListBoxWithScrollBar 中加入 Slider,并且将这个 Slider 摆成直的,放在最右边
就像一般常见的 ScrollBar 那样
<style targettype="CustomControl:ListBoxWithScrollBar">
</style>
到目前为止,ListBoxWithScrollBar 这个控件就只是在 ListBox 上摆了一个毫无反应的 Slider
接下来要让 Slider 变成真正有功能的 ScrollBar
切回 ListBoxWithScrollBar.cs,并在 class name 的上方加入 [TemplatePartAttribute(Name = "ItemNavigateSlider", Type = typeof(Slider))]
TemplatePartAttribute 意在声明 (规定) 这个控件的 Template 中必须要有一个名为 ItemNavigateSlider 的控件,且这个控件是 Slider 类
如此我们便可以在 ListBoxWithScrollBar.cs 中,透过 base.GetTemplateChild("ItemNavigateSlider") as Slider; 来取得这个 Slider 的实例
[TemplatePartAttribute(Name = "ItemNavigateSlider", Type = typeof(Slider))]
public class ListBoxWithScrollBar : ListBox
{
Slider itemSlider;
public ListBoxWithScrollBar()
{
DefaultStyleKey = typeof(ListBoxWithScrollBar);
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
itemSlider = base.GetTemplateChild("ItemNavigateSlider") as Slider;
}
}
取得 Slider 实例后就好办事了
因为 Slider 在拉动时会触发事件 ValueChanged,我们只要听此事件,并在使用者拉动时,对 ListBox 做相对应处理
站在巨人的肩膀上开发,真省事 :D
我希望滑动 Slider 时,可同时将 ListBox 滑动到相对应的 Item
所以 Slider 的最大值应与目前 Items 内的数量一致,并且将 Slider 的最小更动幅度设为 1.0,以符合预期的“Slider 动一格,ListBox 就动一格”
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
itemSlider = base.GetTemplateChild("ItemNavigateSlider") as Slider;
if (itemSlider != null)
{
if (this.Items == null)
{
itemSlider.Visibility = System.Windows.Visibility.Collapsed;
}
else
{
itemSlider.Maximum = this.Items.Count - 1;
itemSlider.SmallChange = 1.0;
itemSlider.LargeChange = 10.0;
itemSlider.Value = itemSlider.Maximum;
itemSlider.ValueChanged += itemSlider_ValueChanged;
}
}
}
当 Slider.ValueChanged 触发时,我们要透过 ListBox 已经撰写好的 ScrollIntoView 方法,将画面卷动至该去的地方
但是,该卷动到哪呢?
ListBox 的 Item,其 Index 是越下面越大
但摆直的 Slider (Orientation="Vertical") ,其值却是越下面越小
这…似乎有点麻烦
只好以差值 (最大值 - 目前值) 的方式来解决此问题
其实 Slider 有一个属性叫 IsDirectionReversed,可以让值的递增递减方向相反,这就留给读者去测试啰
private void itemSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs e)
{
Slider targetSlider = sender as Slider;
if (targetSlider != null)
{
Int32 scrollItemIndex = (Int32)(targetSlider.Maximum - targetSlider.Value);
if (this.Items.Count >= scrollItemIndex)
{
Object targetItem = this.Items.ElementAt(scrollItemIndex);
this.ScrollIntoView(targetItem);
}
}
}
到目前为止,已经实现了拉动 ListBoxWithScrollBar 中的 ScrollBar,快速切换到想看的地方
但是,仍有一些问题需要解决
1. 这个 ScrollBar 长得很突兀,它明明就是直的 Slider
2. 滑动 ListBox,旁边的 ScrollBar 没有跟着移动
本文的范例程序
[Windows Phone 开发] 为 ListBox 增加功能吧 - 加入快速导览 ScrollBar (下)
原文:大专栏 [Windows Phone 开发] 为 ListBox 增加功能吧 - 加入快速导览 ScrollBar (上)