这是
Silverlight入门系列的第四部分。点击
这里可以查看本系列的目录。
你可以下载C#或Visual Basic版本的本示例应用程序的完整项目文件。
在之前的第三部分中,我们做了很多工作来获取公共Web服务返回的数据并让其显示在控件中。我们已经用过了DataGrid控件,但它并不是我们真正想要的用户界面,所以让我们自己来定义一个。为此,我们需要一个ItemsControl和一个DataTemplate。这将让我们学习到XAML的绑定语法,以及如何利用更强大的数据绑定信息。
在开始之前——先删除DataGrid
在开始之前,让我们先删除并且只删除DataGrid,我们也不再需要它的程序集引用以及xmlns,所以也可以大胆删除它们。
把DataGrid替换成ItemsControl,像是这样:
1
<
ItemsControl x:Name
=
"
SearchResults
"
Margin
=
"
0,8,0,0
"
Grid.Row
=
"
1
"
/>
这里我们又要用到Blend了,我们准备在Blend中为ItemControl修改ItemTemplate。ItemControl本质上只是一个按照我们要求工作的解析控件。如果我们仅仅只是把DataGrid替换成ItemControl,那么我们将得到:
ItemControl不知道我们想如何显示数据,所以我们要在模板中告诉它……让我们回到Blend。通常我们都在这里编辑(前面说到过)。
在框中显示发布消息的用户的头像。使用我们之前学到的布局知识可以很容易地创建模板。我们在Blend中放置一个ResultObjects并右击选择编辑ItemTemplate(在已生成项目菜单下)。
现在我们已经获得了一个可以填充内容的空模板。在随后的对话框中,我把它命名为SearchResultsTemplate。现在我们处于布局编辑模式,可以在其中拖拉或移动内容。我创建了一个基于表格的布局,这是我的模板的XAML:
1
<
DataTemplate
x:Key
="SearchResultsTemplate"
>
2
<
Grid
Margin
="4,0,4,8"
d:DesignWidth
="446"
d:DesignHeight
="68"
>
3
<
Grid
.ColumnDefinitions
>
4
<
ColumnDefinition
Width
="Auto"
/>
5
<
ColumnDefinition
Width
="*"
/>
6
</
Grid.ColumnDefinitions
>
7
<
Border
VerticalAlignment
="Top"
Margin
="8"
Padding
="2"
Background
="White"
>
8
<
Image
Width
="40"
Height
="40"
/>
9
</
Border
>
10
11
<
StackPanel
Grid.Column
="1"
VerticalAlignment
="Top"
Margin
="0,4,0,0"
>
12
<
TextBlock
x:Name
="AuthorName"
FontWeight
="Bold"
/>
13
<
Grid
Margin
="0,6,0,0"
>
14
<
Grid
.RowDefinitions
>
15
<
RowDefinition
Height
="Auto"
/>
1
6
<
RowDefinition
Height
="2"
/>
17
<
RowDefinition
Height
="Auto"
/>
18
</
Grid.RowDefinitions
>
19
<
TextBlock
x:Name
="TweetMessage"
TextWrapping
="Wrap"
/>
20
<
TextBlock
x:Name
="PublishDateLabel"
Grid.Row
="2"
/>
21
</
Grid
>
22
</
StackPanel
>
23
</
Grid
>
24
</
DataTemplate
>
我们还把ItemControl放入了ScrollViewer中,因为它本身没有提供滚动条:
1
<
ScrollViewer
Grid.Row
="2"
HorizontalScrollBarVisibility
="Disabled"
VerticalScrollBarVisibility
="Auto"
BorderThickness
="1"
>
2
<
ItemsControl
x:Name
="SearchResults"
Margin
="0,8,0,0"
Grid.Row
="1"
ItemTemplate
="{StaticResource SearchResultsTemplate}"
/>
3
</
ScrollViewer
>
现在我们已经设置完控件了,但是我们还没有告诉它如何处理接收到的数据。
XAML绑定语法
这里是我们写绑定语法的地方。你看,ItemsControl正在获取数据(记住,我们没有改变代码,所以SearchResults.ItemsSource仍然被设置为我们的PagedCollectionView)。我们需要使用绑定来将实体元素映射到模板。基本的XAML绑定语法是:
{Binding Path=<some-data-path>, Mode=<binding mode>}
你还能得到更多高级的功能,但我们将从简单的开始。比如说,将模板中的图像元素绑定到TwitterSearchResult模型的头像,看起来像这样:
1
<
Image
Width
="40"
Height
="40"
Source
="{Binding Path=Avatar, Mode=OneWay}"
/>
同时绑定作者到作者名称元素,像这样:
1
<
TextBlock
x:Name
="AuthorName"
FontWeight
="Bold"
Text
="{Binding Path=Author, Mode=OneWay}"
/>
我们将它们的模式设置成OneWay,因为在不需要改变数据的情况下不需要使用TwoWay。在发布日期的处理上,我们需要提供一些明确的数据格式,这可以通过值转换器做到。
绑定一个值转换器
值转换器是实现IValueConverter接口的一些类,它们提供了正向和反向的转换方法。我们打算在发布日期上采用明确的日期对象格式,所以我们将在项目中一个被叫做转换器的文件夹中创建一个名为DateTimeConverter.cs的类。这个类看起来像这样:
1
using
System;
2
using
System.Threading;
3
using
System.Windows.Data;
4
5
namespace
TwitterSearchMonitor.Converters
6
{
7
/*
8
* Use this converter for formatting dates in XAML databinding
9
* Example:
10
* Text="{Binding Path=PublishDate, Converter={StaticResource DateTimeFormatter}, ConverterParameter=MMM yy}" />
11
*
12
*
*/
13
public
class
DateTimeConverter : IValueConverter
14
{
15
#region
IValueConverter Members
16
17
public
object
Convert(
object
value, Type targetType,
object
parameter, System.Globalization.CultureInfo culture)
18
{
19
DateTime
?
bindingDate
=
value
as
DateTime
?
;
20
21
if
(culture
==
null
)
22
{
23
culture
=
Thread.CurrentThread.CurrentUICulture;
24
}
25
26
if
(bindingDate
!=
null
)
27
{
28
string
dateTimeFormat
=
parameter
as
string
;
29
return
bindingDate.Value.ToString(dateTimeFormat, culture);
30
}
31
32
return
string
.Empty;
33
}
34
35
public
object
ConvertBack(
object
value, Type targetType,
object
parameter, System.Globalization.CultureInfo culture)
36
{
37
throw
new
NotImplementedException();
38
}
39
40
#endregion
41
}
42
}
我们要回到XAML页面来使用它(Search.xaml),还要为此添加xmlns声明和资源。我们要使用的xmlns看起来像这样:
1
xmlns:converters="clr-namespace:TwitterSearchMonitor.Converters"
之后在XAML中的资源节中(其它模板被定义的地方),我们将为转换器添加一个节点:
1
<
navigation:Page
.Resources
>
2
<
converters:DateTimeConverter
x:Key
="DateTimeFormatter"
/>
3
...
之后我们就能在PublishDateLabel元素上使用我们的转换器了,像这样:
1
<
TextBlock
x:Name
="PublishDateLabel"
Text
="{Binding Path=PublishDate,
2
Converter={StaticResource DateTimeFormatter},
3
ConverterParameter=dd-MMM-yyyy hh:mm tt}"
Grid.Row
="2"
/>
这将告诉XAML应该运行IValueConverter来得到输出。我们已经得到了想要的精确的数据格式。所有这些用于绑定的额外语法,使我们得到了期望的解析结果:
没错,我知道用twitpic作为搜索关键字获得了一些有趣的结果,但那是因为用它作为搜索条件获得的数据刷新速度比较快!
很好,不是很难,不是吗?绑定语法对建设你的应用程序来说是至关重要的。
存储一些设置和配置数据
存储最后一次登陆的TwitterID对我们的应用程序来说是很有用的功能,这样当程序下一次启动的时候,就能从上次离开的地方继续。 此外,它还能保存搜索条件历史,使我们可以在历史导航中查看。
为此,我们将使用Silverlight中的独立存储。它可以为存储简单数据开启一个低信用度的用户区域。这里有更多关于独立存储的内容:
使用Silverlight中的独立存储
改变和增加独立存储配额 要做到这一点,我将在Model文件夹中添加一个Helper类。这个辅助类将协助我们在独立存储区域保存和读取数据。独立存储的基础是在其中创建一个文件,并在需要的时候读取或写入数据。在我们的应用中,我们将用独立存储来保存键值对数据(搜索条件/TwitterID)。以下是Helper.cs类的内容:
1
using
System.IO.IsolatedStorage;
2
3
namespace
TwitterSearchMonitor.Model
4
{
5
public
class
Helper
6
{
7
internal
static
string
GetLatestTweetId(
string
searchTerm)
8
{
9
if
(IsolatedStorageSettings.ApplicationSettings.Contains(searchTerm))
10
{
11
return
IsolatedStorageSettings.ApplicationSettings[searchTerm].ToString();
12
}
13
else
14
{
15
return
"
0
"
;
16
}
17
}
18
19
internal
static
void
SaveLatestTweetId(
string
searchTerm,
string
latestId)
20
{
21
if
(IsolatedStorageSettings.ApplicationSettings.Contains(searchTerm))
22
{
23
IsolatedStorageSettings.ApplicationSettings[searchTerm]
=
latestId;
24
}
25
else
26
{
27
IsolatedStorageSettings.ApplicationSettings.Add(searchTerm, latestId);
28
}
29
}
30
}
31
}
现在在Search.xaml.cs中,我们将在活动指针被设置后在SearchForTweetsEx中添加如下内容:
1
_lastId
=
Helper.GetLatestTweetId(SearchTerm.Text);
//
get the latest ID from settings
2
3
Helper.SaveLatestTweetId(SearchTerm.Text, _lastId);
//
saving for history even if a result isn't found
在OnReadCompleted中,当我们关闭XMLReader后,还要添加如下内容:
1
Helper.SaveLatestTweetId(SearchTerm.Text, _lastId);
//
saving last tweet id
如果查找到搜索结果,则将查询条件和TwitterID存入独立存储。
总结
在本章节我们为控件设置了模板,并使用XAML语法绑定了一些简单的数据,还为格式化视图中的信息添加了一个值转换器,最后还用独立存储机制保存了设置信息。
现在我们的程序已经可以正常工作了,接下来让我们为它锦上添花。
让我们进入第五部分,我们将为本章节获得的数据添加一些新的控件以提高用户体验。