来一个邪恶的RP测试工具(下)
在上一篇日志里,我实现了一个简单的RP测试工具,接下来,我需要完善它:将名字输入栏修改成下拉框形式:存储最新输入的十个名字:
修改布局
添加一个ToggleButton:
<ToggleButton HorizontalContentAlignment="Right" Grid.Column="1" Height="25" HorizontalAlignment="Right" VerticalAlignment="Center" Width="20" x:Name="RPToogleButton"> <Path Height="4" HorizontalAlignment="Center" Margin="0,0,4,0" x:Name="BtnArrow" Width="8" Stretch="Uniform" VerticalAlignment="Center" Data="F1 M 301.14,-189.041L 311.57,-189.041L 306.355,-182.942L 301.14,-189.041 Z "> <Path.Fill> <SolidColorBrush Color="#FF333333" x:Name="BtnArrowColor"/> </Path.Fill> </Path> </ToggleButton>
插入Popup控件,设置Popup位置如图,在Popup控件上添加一个ListBox控件,并调整其大小:
我们需要将ToggleButton的IsChecked和Popup控件的IsOpen绑定在一起。
添加绑定
和WPF不同,Silverlight不支持UI元素间的互相绑定。因此,需要添加一个“绑定帮助对象”数据源,ToolButton的IsChecked属性和Popup的IsOpen属性分别双向绑定到该数据源上,来实现联动效果:
public class BindingHelper : INotifyPropertyChanged { protected bool isToggleChecked = false; public bool IsToggleChecked { get { return isToggleChecked; } set { if (isToggleChecked!=value) { isToggleChecked = value; } OnPropertyChanged("IsToggleChecked"); } } private void OnPropertyChanged(string p) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(p)); } } public event PropertyChangedEventHandler PropertyChanged; }
在Popup控件的IsOpen属性中,选择数据绑定:
在创建数据绑定对话框中点击添加CLR对象按钮,选择数据源类型:
点击确定,回到创建数据绑定对话框,选中刚才添加的数据源,在绑定方向中选择TwoWay:
<Popup IsOpen="{Binding Mode=TwoWay, Path=IsToggleChecked, Source={StaticResource BindingHelperDS}}"> <ToggleButton IsChecked="{Binding Mode=TwoWay, Path=IsToggleChecked, Source={StaticResource BindingHelperDS}}">
添加数据源
接下来为ListBox添加数据源,当点击ToggleButton时,在弹出的ListBox中显示最新测试过的10个人的RP值。
由于需要双向通信,我选择ObservableCollection<T>作为数据源,在后台代码中添加代码如下:
public class RPValue { public String Name { get; set; } public int rp { get; set; } } public class RPCollection : ObservableCollection<RPValue> { }
使用RPCollection而不直接使用ObservableCollection的原因是,Silverlight的XAML里不支持直接使用泛型对象,而通过继承,我们就可以在Expression Bland的添加CLR对象数据源对话框中直接找到RPCollection这个类:
把ListBox的ItemsSource绑定到新添加的数据源,将绑定方向设置为双向,然后点击数据模板按钮。
由于在Expression Bland2生成的数据模板中 ,StackPanel是垂直排列的,因此需要修改一下,可以在资源里面选中添加的模板,然后在设计器里修改:
<DataTemplate x:Key="RPCollectionTemplate"> <StackPanel Orientation="Horizontal"> <TextBlock Text="{Binding Path=Name}" Width="80" FontSize="12"/> <TextBlock Text="{Binding Path=rp}" FontSize="12" Foreground="#FFFF0000"/> </StackPanel> </DataTemplate>
响应选择操作
为了方便操作,我添加了两个get属性来获取数据源对象:
public RPCollection RPs { get { return Resources["RPCollectionDS"] as RPCollection; } } public BindingHelper bind { get { return Resources["BindingHelperDS"] as BindingHelper; } }
添加ListBox的SelectionChanged事件处理方法:当ListBox中某一项被选中时,用选中的项替换TextBox的名字,并关闭ListBox:
private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e) { var lb = sender as ListBox; if (lb.SelectedItem != null) { NameTextBox.Text = (lb.SelectedItem as RPValue).Name; } bind.IsToggleChecked = false; }
保存输入的名字和结果
在RPCollection中添加一个InsertItem方法,用来插入结果最新的计算结果:
public void InsertItem(RPValue value) { if (this.SingleOrDefault(x => x.Name == value.Name) == null) { if (this.Count == 10) { RemoveAt(9); } InsertItem(0, value); } }
将结果保存到计算机
和Flex一样,Silverlight提供了独立存储技术(IsolatedStorage),用来在宿主计算机中保存一定量的数据,即使在是浏览器关闭的情况下,保存的数据依然存在。
首先把RPCollection转换成String:
public String ToText() { String text = String.Empty; foreach (var v in this) { text += (v.Name + "," + v.rp.ToString() + "|"); } return text.Substring(text.Length - 1); } public void FromText(String text) { Clear(); foreach (var t1 in text.Split('|')) { var t2=t1.Split(','); Add(new RPValue() { Name = t2[0], rp = int.Parse(t2[1]) }); } }
分别在构造函数里添加读取独立存储文件的过程和在每次添加Item的时候保存文件的过程,
读取存储文件
public RPCollection() { if (store.FileExists(fname)) { using (IsolatedStorageFileStream isfs = new IsolatedStorageFileStream(fname, FileMode.OpenOrCreate, store)) { using (StreamReader streamReader = new StreamReader(isfs)) { FromText(streamReader.ReadLine()); } } } }
保存文件
public void InsertItem(RPValue value) { if (this.SingleOrDefault(x => x.Name == value.Name) == null) { if (this.Count == 10) { RemoveAt(9); } InsertItem(0, value); if (store.FileExists(fname)) { store.DeleteFile(fname); } using (IsolatedStorageFileStream isfs = new IsolatedStorageFileStream(fname, FileMode.OpenOrCreate, store)) { using (StreamWriter streamWriter = new StreamWriter(isfs)) { streamWriter.WriteLine(ToText()); streamWriter.Flush(); } } } }
参考文献及源码下载
Silverlight vs Flash: Local Storage
Silverlight 2.0 beta1 and WPF Data Binding – what’s the same and what’s different?