还是以上篇导航为基础,完善导航的页面并为导航的页面绑定数据和显示数据。
这篇学习笔记主要记录两个点,分别为如下:
- 基于Silverlight 的Windows Phone 数据绑定
- 为应用程序添加程序栏,额外记录显示和隐藏系统托盘(System.Tray)的代码
1.数据绑定
数据绑定为基于Silverlight 的应用程序提供了一个简单的方式来实现显示和数据之间交互。数据显示从数据管理中分离出来。用户界面与数据模型之间的连接或者绑定,允许数据在两者之间流动。不一个绑定被建立,数据发生改变,被绑定到数据的UI(用户界面 )元素能自动发生改变。这本篇 学习笔记将针对Silverlight 提供的控件DataList 为原型讲述如何绑定数据和删除数据。下面的图显示了绑定的概念:
上图可以分为绑定目标和绑定源。
- 绑定目标代表绑定的对象是UI控件,所以必须控件是FrameworkElement的任何一个DependencyProperty(提供快速的方法计算值,可与其他动态数据交互)。
- 绑定来源包含了来源和目标之间的数据流的数据。来源可以是任何运行于CLR的对象,包括目标元素本身或者其他UI元素。
- 数据流的方向。Binding 对象的Mode属性设置了数据流的方向。分为三种方向:OneTime---目标控件属性只是更新一次,以后的更新会被忽略。oneWay--数据对象的值会同步到目标控件的属性,但是目标控件的属性的改变不会同步到数据对象中。TwoWay--目标控件的属性和数据对象的值相互同步。
- 可选的值转换器,它被应用于经过值转换器传递的数据。值转器是一个类,它实现了IValueConverter。
原理大致如上,下面看一下要实现的效果图:
如图,点击图中画红圈的连接,将会导航到一个放置DataList的数据列表,DataList放置了一列带图片和文字的数据,选中某项则让删除按钮可用,可以指定删除某项数据,并实时更新到列表中,界面效果如下:
绑定数据原理:
为了保证引用的集合发生改变时数据绑定机制也能使集合接收通知,Silverlight 使用了事件机制。数据绑定引擎期望当一个集合的值改变时,所发生的事件是CollectionChanged ,该事件被定义为如下:
这个集合的实现应该是集合的每一个改变(添加/编辑/移除集合的成员,程序顺序,等)都会被触发到事件。引用事件响应变化。这个事件被定义到INotifyCollectionChanged 接口中。 为使数据绑定能自动更新到集合,应该创建自己的集合并实现这个接口。在Silverlight 类库中提供了两个集合,这两个集合实现了这个接口,我们可以很放心的拿来使用,分别为:ObservableCollection<T>和ReadOnlyObservableCollection<T> 。
- ObservableCollection -代表了一个动态数据集。它会为集合中的项发生添加,移除或者整个列表被刷新等情况实时提供通知。
- ReadOnlyObservableCollection -代表了一个可读的ObservableCollection。
Tip:两个类的数据绑定机制会对更新己经绑定到集合的对象时触发的事件做出响应。
实现这个集合之前,我们有必要先完成一个Phots 的属性集,代码如下:
{
private string _Filename;
public string Filename
{
get { return _Filename; }
set
{
_Filename = value;
NotifyPropertyChanged("FileName");
}
}
private BitmapImage _Image;
public BitmapImage Image
{
get { return _Image; }
set
{
_Image = value;
NotifyPropertyChanged("Image");
}
}
private void NotifyPropertyChanged(string propertyName)
{
if (null != PropertyChanged)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
TIP: INotifyPropertyChanged ,是一个接口实现它是为了在数据改变的时候能够广播出去,通过属性名称通知哪个属性名称发生了变化。
另外我们还需要一个工具来,用来通过资源的URI将文件转为可让图片接收的BitmapImage类型,代码如下:
{
public static BitmapImage GetImage(string filename)
{
string imgLocation = Application.Current.Resources["ImagesLocation"].ToString();
StreamResourceInfo p_w_picpathResource = Application.GetResourceStream(new Uri(imgLocation + filename, UriKind.Relative));
BitmapImage p_w_picpath = new BitmapImage();
p_w_picpath.SetSource(p_w_picpathResource.Stream);
return p_w_picpath;
}
完成了上述两个类,打开图片对应的Xaml,我这里的名称路径在:/Views/PicturesView/Default.xaml。在该页面的ContentPanel中加入一个DataList 控件,并为其数据模板添加内容显示窗器,比如本例以横向显示的StackPanel中放置了一个图片控件和一个文本控件用来显示图片和图片名称,XML代码如下:
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal">
<Button x:Name="btnRemoveSelection" Content="Remove Image" Click="btnRemoveSelection_Click" IsEnabled="False"/>
</StackPanel>
<ListBox x:Name="lstPictures" Width="450" Height="520" Margin="10" SelectionChanged="lstPictures_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding Image}" Width="100" Stretch="Uniform" HorizontalAlignment="Center"/>
<TextBlock Text="{Binding Filename}" TextWrapping="Wrap"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</Grid>
做Asp.net 的朋友赶快过来围观,这个DataList 数据绑定的方法不是就是ASP.NET常用的写法吗?只是来源做了处理使用Silverlight 的写法,不过看样子和格式都与Asp.Net 的DataList 数据绑定很相似。好了,之后进入CS文件,引用这一命名空间:
声明上述提到的ObservableCollection对象,将Photo对象放进去,我在做Android 的时候喜欢写数据源的时候这样写:List<HashMap<String,Object>> list=new ArrayList<HashMap<String,Object>>(); 然后去自定义一个数据源,感觉灵活性比较强,WP7还没试过,就先暂时使用这种数据对象来实现数据的更新显示吧。
定义好后,为集合中的PHOTO对象存值和设置DataList的事件并与删除按钮交互,所有代码如下:
/// 此事件定义了当数据改变时通知集合触发事件
/// </summary>
// public event NotifyCollectionChangedEventHandler CollectionChanged;
ObservableCollection<Photo> photos = new ObservableCollection<Photo>();
private void initializePhotos()
{
photos.Add(new Photo() {
Filename="Butterfly.jpg",
Image=Utils.GetImage("Butterfly.jpg")
});
photos.Add(new Photo()
{
Filename = "Chrysanthemum.jpg ",
Image = Utils.GetImage("Chrysanthemum.jpg")
});
photos.Add(new Photo()
{
Filename = "Desert.jpg",
Image = Utils.GetImage("Desert.jpg")
});
photos.Add(new Photo()
{
Filename = "Field.jpg",
Image = Utils.GetImage("Field.jpg")
});
photos.Add(new Photo()
{
Filename = "Tulips.jpg",
Image = Utils.GetImage("Tulips.jpg")
});
photos.Add(new Photo()
{
Filename = "Flower.jpg",
Image = Utils.GetImage("Flower.jpg")
});
photos.Add(new Photo()
{
Filename = "Hydrangeas.jpg ",
Image = Utils.GetImage("Hydrangeas.jpg")
});
photos.Add(new Photo()
{
Filename = "Butterfly.jpg",
Image = Utils.GetImage("Butterfly.jpg")
});
photos.Add(new Photo()
{
Filename = "Jellyfish.jpg",
Image = Utils.GetImage("Jellyfish.jpg")
});
photos.Add(new Photo()
{
Filename = "Koala.jpg",
Image = Utils.GetImage("Koala.jpg")
});
photos.Add(new Photo()
{
Filename = "Leaves.jpg",
Image = Utils.GetImage("Leaves.jpg")
});
photos.Add(new Photo()
{
Filename = "Lighthouse.jpg",
Image = Utils.GetImage("Lighthouse.jpg")
});
photos.Add(new Photo()
{
Filename = "Penguins.jpg",
Image = Utils.GetImage("Penguins.jpg")
});
photos.Add(new Photo()
{
Filename = "Rocks.jpg",
Image = Utils.GetImage("Rocks.jpg")
});
photos.Add(new Photo()
{
Filename = "Tulip.jpg",
Image = Utils.GetImage("Tulip.jpg")
});
photos.Add(new Photo()
{
Filename = "Window.jpg",
Image = Utils.GetImage("Window.jpg")
});
}
public Default()
{
InitializeComponent();
initializePhotos();
lstPictures.ItemsSource = photos;
}
private void btnRemoveSelection_Click(object sender, RoutedEventArgs e)
{
if (null!=lstPictures.SelectedItem)
{
photos.Remove(lstPictures.SelectedItem as Photo);
}
}
private void lstPictures_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (null != lstPictures.SelectedItem)
{
btnRemoveSelection.IsEnabled = true;
}
if (photos.Count==0)
{
btnRemoveSelection.IsEnabled = false;
}
}
2.应用程序栏
在很多情况下我们会需要提供跨应用程序的通用功能。一些情况下,我们可能想要为页面提供一些符合某些条件时才出现的特殊功能。另外一些情况下,您只希望获得更多的屏幕空间,这些情况下,Windows Phone 提供了应用程序栏。
要为应用程序添加应用程序栏,需要在App.xaml 注册。本篇学习的内容注册一个菜单和两个 按钮,一个菜单导航到About 页面,另外一个按钮跳转到Image列表。打开App.xaml,在Application.Resouces节点内,输如下代码并为其添加事件响应:
<shell:ApplicationBar x:Key="MainAppBar" IsVisible="True">
<!--菜单项 Abou-->
<shell:ApplicationBar.MenuItems>
<shell:ApplicationBarMenuItem Text="About" Click="ApplicationBarMenuItem_Click"/>
</shell:ApplicationBar.MenuItems>
<!--应用程序条 按钮-->
<shell:ApplicationBar.Buttons>
<shell:ApplicationBarIconButton Text="Web" IconUri="ie_icon.png" Click="ApplicationBarIconButton_Click"/>
<shell:ApplicationBarIconButton Text="Images" IconUri="pictures_Icon.png" Click="ImageEvent_Click"/>
</shell:ApplicationBar.Buttons>
</shell:ApplicationBar>
进入APP.xaml.cs在自动生成的事件句柄内,键入如下代码实现跳转:
{
PhoneApplicationFrame root = Application.Current.RootVisual as PhoneApplicationFrame;
root.Navigate(new Uri("/About", UriKind.Relative));
}
private void ApplicationBarIconButton_Click(object sender, EventArgs e)
{
}
private void ImageEvent_Click(object sender, EventArgs e)
{
PhoneApplicationFrame root = Application.Current.RootVisual as PhoneApplicationFrame;
root.Navigate(new Uri("/Pictures",UriKind.Relative));
}
在你需要显示应用程序栏的页面的phone:PhoneApplicationPage节点指定ApplicationBar来源,比如本文的指定方式:
x:Class="DataBind.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="768"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
shell:SystemTray.IsVisible="True"
ApplicationBar="{StaticResource MainAppBar}"
>
看看截图效果吧:
源码比较大,有需要的EMAIL我:terryyhl@gmail.com