您的位置:首页 > 其它

博客园客户端UAP开发随笔--自定义控件的左膀右臂

2015-01-05 12:37 375 查看

前言

我们上一次说到了App的精灵:自定义控件。这一次,我们接着这一话题,说说自定义控件的两个得力助手:

选择器 - TemplateSelector

转换器 – Converter

这两个东西能帮助自定义控件更为简单方便地被使用,所以必须掌握。

数值转换器 Converter

这个大家可能不陌生,因为在MSDN里,介绍到Data Binding时,总会顺带着介绍一下数据转换,比如这个网页:

http://msdn.microsoft.com/library/windows/apps/xaml/hh464965.aspx/

最后的那个StringFormatter就是。

在博客园UAP中,我们使用了好几个Converter,每个Converter实际上是一段cs类,派生自IValueConverter接口。它们都放在CNBlogs.Shared项目的ControlHelper目录中。我们用其中的两个做例子来说明一下。

Bool/Visibility的转换

我们看这段代码:

public class BoolToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
return (bool)value ? Visibility.Visible : Visibility.Collapsed;
}

public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}


功能很直接,就是把bool值转换成Visibility的枚举值,然后绑定到一个控件的某个元素上,比如,在XAML里:

<Style TargetType="local:FavoriteAuthorControl">
<Setter Property="Template">
<Setter.Value>

……
<TextBlock Grid.Column="0" Text="" FontFamily="Segoe UI Symbol" Foreground="Red" Visibility="{Binding Converter={StaticResource BoolToVisiblityConverter}, Path=HasNew}"/>

……
</Setter.Value>
</Setter>
</Style>


上面这段XAML是一个自定义控件的style描述,控件本身是“收藏”页中的“关注的博主”控件,其目的是:当HasNew字段值是true时,在博主的头像左侧显示一个红色的小星星,表示该博主有新的博客发表了,你如果关注了该博主,可以点进去看他的新博客。当HasNew是false时,不显示小星星,你就不需要进去看一圈,发现没有新博客然后失望地离开了。

有的人也许会说:我在FavoriteAuthorControl.cs的code里,直接取HasNew值,看其是true还是false,然后直接用code写Visibility = Visiable or Collapsed即可。

这样做当然也可以啦,但是有几个不方便的地方:

1)你需要写code

2)读你的程序的人,如果没读到你的code,就不知道这个逻辑

3)你需要命名那个TextBlock (x:Name=…)然后在code里用GetChildTemplate(“…")来得到这个控件,再指定其Visibility值。

4)在其它控件中,你依然还要写code做类似的事

而用我们推荐的方法,其好处显而易见:

1)在XAML中绑定即可,no code

2)这个转换器可以重用

3)无需更多定义,在binding阶段一次性解决(这也是WPF的优点之一),性能无需考虑

4)可读性极好

bool/string转换器

再举一个例子巩固一下,上code:

class NightModeLabelConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
bool bValue = (bool)value;
if (bValue)
{
return "日间模式";
}
else
{
return "夜间模式";
}
}

public object ConvertBack(object value, Type targetType, object parameter, string culture)
{
throw new NotImplementedException();
}
}


在XAML中这样使用:

<AppBarButton x:Name=”btn_Mode”Label=”{Binding Converter={StaticResource NightModeLabelConverter}, Path=Settings.NightMode”/>


这个绑定过程会先检查Setting中NightMode的值,如果是True,会在按钮下方显示“日间模式”;如果是false,会显示“夜间模式”。

模板选择器 TemplateSelector

模板选择器很少有人用到,因为介绍它的文档很少,到目前为止我们没发现,也是一个偶然的机会知道有这么个东西的,很好用!记得有几次面试,我们都问到了这个问题:如果一条新闻有图片,另一条新闻没图片,你准备如何显示这两个Control呢?如下图所示,上面那个有图片(特意用红色矩形标记了一下),下面那个没图片。





我们得到两种回答:

1)做成一个通用的Control,然后根据有无图片的URL自动显示右侧的图片

2)用code在自定义空间里控制右侧图片是否显示

当然可以!这些都能实现这个目的。但要注意图片左侧还有一条灰色竖线哟。

我们今天推荐一个更为标准通用的方法:TemplateSelector。

先上code:

namespace CNBlogs.ControlHelper
{
class NewsControlTemplateSelector : DataTemplateSelector
{
public DataTemplate dtHasImage { get; set; }
public DataTemplate dtNoImage { get; set; }

protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
{
CNBlogs.DataHelper.DataModel.News news = item as CNBlogs.DataHelper.DataModel.News;
if (string.IsNullOrEmpty(news.TopicIcon))
{
return dtNoImage;
}
return dtHasImage;
}
}
}


在Shared project中,ControlHelper目录下,有个NewsControlTemplateSelector.cs。它从DataTemplaterSelector父类派生出来,先定义了两个DataTemplate: dtHasImage和dtNoImage,分别对应有无图片的两种情况。然后在SelectTemplateCore重写方法中,先获得news实例,判断TopicIcon是否存在,如果存在,返回dtHasImage;否则返回dtNoImage。

在MainPage.xaml中,这样使用:

<Page.Resources>
......
<DataTemplate x:Key="NewsNoImageTemplate">
<local:NewsTitleTextControl/>
</DataTemplate>

<DataTemplate x:Key="NewsHasImageTemplate">
<local:NewsTitleTextImageControl/>
</DataTemplate>
......
</Page.Resources>


首先在Page.Resource中定义两个Template,给出两个不一样的Key值,一个叫做“NewsNoImageTemplate”,另一个叫做“NewsHasImageTemplate”,分别对应两个自定义control:NewsTitleTextControl和NewsTitleTextImageControl (在Theme\Generic.xaml中定义的,一个只有标题文本,另一个有标题文本和右侧图片)。

然后在News PivotItem的ListView中这样写:

<PivotItem Margin="0" Tag="news">
......
<ListView.ItemTemplateSelector>
<ControlHelper:NewsControlTemplateSelector
dtHasImage="{StaticResource NewsHasImageTemplate}"
dtNoImage="{StaticResource NewsNoImageTemplate}"/>
</ListView.ItemTemplateSelector>
</ListView>
</PivotItem>


ListView.ItemTemplateSelector用于定义当前的新闻用什么样的模板来显示。注意下面的语法:

1)ControlHelper:NewsControlTemplateSelector对应第一段cs代码的类名

2)dtHasImage/dtNoImage对应cs代码中的两个返回值

3)StaticResource NewsHasImageTemplate对应Page.Resources中的x:Key=”NewsHasImageTemplate”,间接对应到了有图片的自定义控件模板。

即:控件<—>资源文件<—>选择器。这三者的关系容易搞混,尤其是名字,特别容易起错,以至于最后自己都不记得什么对应什么。建议是控件用Control做结尾,如NewTitleTextControl;资源文件的Key用Template做结尾,如NewsNoImageTemplate;选择器用dt做开始,如dtNoImage。这样的话,如果有图片,Selector就会返回dtHasImage,对应到了本页资源中的StaticResource NewHasImageTemplate,从而使用NewsTitleTextImage自定义控件来显示该条新闻。

小结

好了,掌握了第一个转换器,我们不用再每个control里都写code了,直接在XAML中绑定即可。掌握了第二个选择器,我们可以充分分解页面逻辑,把它们颗粒化到一个合理的程度,不需要再考虑如何用一个控件显示两种或多种样式。

但是要注意,在以前的“App精灵”随笔中,提到的PostControl控件,有时要显示作者头像,有时要显示“朕已阅”等等,如果你要做成5个Control,再用TemplateSelector来选择就是不对的了。基本原则是:在一个ListView中,如果有两种以上的样式选择,用TemplateSelector;在两个页面中,用TemplateBinding外部配置来指定要不要显示某个元素,比如AuthorAvatar.Visibility = {TemplateBinding ShowAvatar},使用它时在两个页面中分别直接指定Visable或Collapsed即可,从而允许使用一个控件搞定5种情况。

在Windows Phone 8.1和Windows 8.1中,一点儿区别都没有,所以我们把这些cs都放在了shared project中,以便能够在两个项目中共享。

分享代码,改变世界!

Windows Phone Store App link:

http://www.windowsphone.com/zh-cn/store/app/博客园-uap/500f08f0-5be8-4723-aff9-a397beee52fc

Windows Store App link:

http://apps.microsoft.com/windows/zh-cn/app/c76b99a0-9abd-4a4e-86f0-b29bfcc51059

GitHub open source link:

https://github.com/MS-UAP/cnblogs-UAP

MSDN Sample Code:

https://code.msdn.microsoft.com/CNBlogs-Client-Universal-477943ab

MS-UAP

2015/1/5
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐