Windows Phone 十、数据绑定

数据绑定:是一种 XAML 和后台数据交互的方式(桥梁)
通过后台进行数据绑定
     <Grid>
<TextBox
x:Name="txtHello"
Text="{Binding}"
Header="{Binding Path=Value}"/>
<Button
x:Name="btnHello"
Content="按钮"
Click="btnHello_Click"/>
</Grid>
     public class MainPageData
{
public string Value { get; set; }
}

MainPageData

         public MainPageData Data { get; set; }
private void btnHello_Click(object sender, RoutedEventArgs e)
{
Data = new MainPageData();
Data.Value = DateTime.Now.ToString();
//当前页面对象,数据上下文可以被“继承”
this.DataContext = Data;
}

通过前台进行数据绑定

 <Page
x:Class="Myapp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Myapp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" DataContext="{Binding Data,RelativeSource={RelativeSource Self}}" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel>
<TextBox Text="{Binding Text1}"/>
<TextBox Text="{Binding Text2}"/>
</StackPanel>
</Page>
     public class DataBindingPageData
{
public string Text1 { get; set; }
public string Text2 { get; set; }
}

DataBindingPageData

     public sealed partial class MainPage : Page
{
//当前页面数据源
public DataBindingPageData Data { get; set; }
public MainPage()
{
//该操作必须在页面初始化之前
Data = new DataBindingPageData();
Data.Text1 = "Hello";
Data.Text2 = "World";
//界面元素初始化操作
this.InitializeComponent();
this.NavigationCacheMode = NavigationCacheMode.Required;
}
数据绑定的概念
 <Page
x:Class="Myapp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Myapp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" DataContext="{Binding Data,RelativeSource={RelativeSource Self}}" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel>
<TextBox Text="{Binding Text1.Value}"/>
<TextBox Text="{Binding Text2}"/> <TextBox x:Name="tb1"/>
</StackPanel>
</Page>
 namespace MyApp
{ /// <summary>
/// 可用于自身或导航至 Frame 内部的空白页。
/// </summary>
public sealed partial class MainPage : Page
{
//当前页面数据源
public DataBindingPageData Data { get; set; }
public MainPage()
{
//该操作必须在页面初始化之前
Data = new DataBindingPageData();
Data.Text1 = new SubData { Value = "Hello" };
Data.Text2 = "World";
//界面元素初始化操作
this.InitializeComponent();
this.NavigationCacheMode = NavigationCacheMode.Required;
}
/// <summary>
/// 在此页将要在 Frame 中显示时进行调用。
/// </summary>
/// <param name="e">描述如何访问此页的事件数据。
/// 此参数通常用于配置页。</param>
protected override void OnNavigatedTo(NavigationEventArgs e)
{
#region 数据绑定四个组成部分(目标.依赖属性{绑定}数据源.数据源属性)
//绑定桥梁(两头分别是谁)
Binding binding = new Binding();
//指定数据源和数据源属性
binding.Source = Data;
binding.Path = new PropertyPath("Text2");
//指定绑定的目标依赖属性
tb1.SetBinding(TextBox.TextProperty, binding);
#endregion
}
}
public class DataBindingPageData
{
public SubData Text1 { get; set; }
public string Text2 { get; set; }
}
public class SubData
{
public string Value { get; set; }
}
}
数据上下文(DataContext)
     <Page.Resources>
<SolidColorBrush x:Key="color" Color="AliceBlue"/>
<!--资源中定义数据源对象-->
<local:ResourceBindingPageData
x:Key="myData"
Value="Hello World"/>
</Page.Resources>
<Grid DataContext="{StaticResource myData}">
<TextBox Text="{Binding Value}"/>
</Grid>
     public class ResourceBindingPageData
{
public string Value { get; set; }
}
元素值绑定

数据绑定的数据源不仅仅可以是一个自定义对象,还可以使用 UI 元素对象

就是将一个界面元素对象作为绑定的数据源,轻松实现两个控件之间的交互

基本语法:

Width="{Binding ElementName=sliderDiameter, Path=Value}"

ElementName 就是指定数据源控件的 Name 属性

     <StackPanel>
<Slider
x:Name="slider"
Minimum="50"
Maximum="300"/>
<Ellipse
x:Name="ellipse"
Width="{Binding ElementName=slider,Path=Value}"
Height="{Binding ElementName=slider,Path=Value}"
Fill="HotPink"/>
</StackPanel>
绑定数值类型转换

当数据源和目标属性类型或格式不相同时,比如URL链接,可以自定义两者之间的转换方式

具体就是实现 IValueConverter 接口

     <Page.Resources>
<local:BookIdToBookUrlConverter x:Key="BookIdToBookUrlConverter"/>
</Page.Resources>
<Grid>
<TextBox Text="{Binding BookId,Converter={StaticResource BookIdToBookUrlConverter}}"/>
</Grid>
 namespace MyApp
{
/// <summary>
/// 可用于自身或导航至 Frame 内部的空白页。
/// </summary>
public sealed partial class MainPage : Page
{
public ValueConverterPegaData Data { get; set; }
public MainPage()
{
Data = new ValueConverterPegaData { BookId = };
//Data.BookUrl = string.Format("http://www.itcast.cn/book/{0}", Data.BookId);
this.InitializeComponent();
this.NavigationCacheMode = NavigationCacheMode.Required;
this.DataContext = Data;
} /// <summary>
/// 在此页将要在 Frame 中显示时进行调用。
/// </summary>
/// <param name="e">描述如何访问此页的事件数据。
/// 此参数通常用于配置页。</param>
protected override void OnNavigatedTo(NavigationEventArgs e)
{
// TODO: 准备此处显示的页面。 // TODO: 如果您的应用程序包含多个页面,请确保
// 通过注册以下事件来处理硬件“后退”按钮:
// Windows.Phone.UI.Input.HardwareButtons.BackPressed 事件。
// 如果使用由某些模板提供的 NavigationHelper,
// 则系统会为您处理该事件。
}
}
public class ValueConverterPegaData
{
public int BookId { get; set; }
public string BookUrl { get; set; }
}
public class BookIdToBookUrlConverter : IValueConverter
{
//从原数据转换成目标数据
public object Convert(object value, Type targetType, object parameter, string language)
{
return string.Format("http://www.itcast.cn/book/{0}", value);
}
//从目标数据转换成原数据
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
return null;
}
}
}

IValueConverter接口实现数值类型转换

     <StackPanel>
<StackPanel.Resources>
<local:ValueToColorConverter x:Key="ValueToColorConverter"/>
</StackPanel.Resources>
<Slider
x:Name="slider"
Minimum="0"
Maximum="255"/>
<Ellipse
x:Name="ellipse"
Width="{Binding ElementName=slider,Path=Value}"
Height="{Binding ElementName=slider,Path=Value}"
Fill="{Binding ElementName=slider,Path=Value,Converter={StaticResource ValueToColorConverter}}"/>
</StackPanel>
     public class ValueToColorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
var d = (double)value;
var b = (byte)d;
return new SolidColorBrush(Color.FromArgb(, b, b, b));
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
return null;
}
}
绑定模式

OneTime:一次绑定,即数据上下文初始化时更新目标属性

OneWay:单向绑定,仅当数据源属性发生变更时更新目标属性

TwoWay:双向绑定,当目标属性发生变更通知数据源变更(同步)

     <Grid x:Name="LayoutRoot">

         <Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions> <!-- 标题面板 -->
<StackPanel Grid.Row="0" Margin="19,0,0,0">
<TextBlock Text="MY APPLICATION" Style="{ThemeResource TitleTextBlockStyle}" Margin="0,12,0,0"/>
<TextBlock
Text="绑定模式"
Margin="0,-6.5,0,26.5"
Style="{ThemeResource HeaderTextBlockStyle}"
CharacterSpacing="{ThemeResource PivotHeaderItemCharacterSpacing}"/>
</StackPanel> <!--TODO: 应将内容放入以下网格-->
<Grid Grid.Row="1" x:Name="ContentRoot" Margin="19,9.5,19,0">
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions> <Slider
x:Name="slider"
Grid.Row="0"
Value="10"/> <!--默认形式是OneWay-->
<TextBox
Grid.Row="1"
Header="Default"
Text="{Binding ElementName=slider, Path=Value}"/> <!--OneTime是在进行数据绑定时同步一次-->
<TextBox
Grid.Row="2"
Header="One Time"
Text="{Binding ElementName=slider, Path=Value, Mode=OneTime}"/> <!--OneWay会实时同步数据源变化,但是不会将自身变化同步到数据源-->
<TextBox
Grid.Row="3"
Header="One Way"
Text="{Binding ElementName=slider, Path=Value, Mode=OneWay}"/> <!--OneWay会实时同步数据源变化,会将自身变化同步到数据源-->
<TextBox
Grid.Row="4"
Header="Two Way"
Text="{Binding ElementName=slider, Path=Value, Mode=TwoWay}"/> </Grid>
</Grid>

数据变更通知(一)

使用自定义数据源进行数据绑定时,不管绑定模式是哪一种,都不会造成目标对象实时同步

如果想要实现数据变更实时同步,就必须要让自定义数据类型实现 INotifyPropertyChanged 接口

INotifyPropertyChanged 具有 PropertyChanged 的事件,在数据源被绑定时注册,数据源变更时被触发(需要自己写代码手动触发)

数据变更通知(二)

TwoWay 绑定会在目标和源中的任一个发生更改时同时更新目标和源

此行为的一个例外是,在每次用户击键之后,不会将 TextBox.Text 的更改发送给绑定源

除非将 Binding.UpdateSourceTrigger 设置为 PropertyChanged

默认情况下,仅当 TextBox 失去焦点时才发送更改

INotifyPropertyChanged 封装

     <StackPanel>
<TextBlock/>
<TextBox/>
<Slider/>
</StackPanel>
     public class NotifyPropertyChangedPageViewModel : ViewModelBase
{
public string Text1 { get; set; }
private string text2;
public string Text2
{
get { return text2; }
set
{
SetProperty(ref text2, value);
}
}
public double number;
public double Number
{
get { return number; }
set
{
SetProperty(ref number, value);
}
}
private string hello;
public string Hello
{
get { return hello; }
set
{
SetProperty(ref hello, value);
}
}
}
public abstract class ViewModelBase : NotifyPropertyChanged
{
protected void SetProperty<TProperty>(ref TProperty current, TProperty value, [CallerMemberName]string propertyName = null)
{
if (object.Equals(current, value))
{
//属性值没有发生变化,减轻应用程序负担
return;
}
current = value;
OnPropertyChanged(propertyName);
}
}
public abstract class NotifyPropertyChanged : INotifyPropertyChanged
{
/// <summary>
/// 当属性发生变化时调用
/// </summary>
/// <param name="propertyName"></param>
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null && !string.IsNullOrEmpty(propertyName))
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
集合数据绑定(列表控件元素)

列表控件主要是 ListBox、ListView、GridView 等

为列表控件绑定数据不再是为 DataContext 属性赋值,应该使用列表控件自有的 ItemsSource 属性

当列表数据元素由 ItemsSource 绑定,就不能再动态操作(增删改) Items 属性

ObservableCollection<T>

绑定集合元素和普通绑定一样,界面显示默认不会跟随数据源变化而变化

如果要实现一个动态绑定的列表,通常我们需要用到 ObservableCollection<T> 类型作为数据源集合的类型

     <Grid>
<ListView
x:Name="list"
ItemsSource="{Binding DateTimes}"/>
<Button
Content="加载更多"
Click="Button_Click"/>
</Grid>
     public sealed partial class MainPage : Page
{
public MainPageViewModel ViewModel { get; set; }
public MainPage()
{
ViewModel = new MainPageViewModel();
ViewModel.DateTimes = new ObservableCollection<string>();
for (int i = ; i < ; i++)
{
ViewModel.DateTimes.Add(string.Format("{0}\t{1}", i, DateTime.Now));
}
this.InitializeComponent();
this.NavigationCacheMode = NavigationCacheMode.Required;
}
/// <summary>
/// 在此页将要在 Frame 中显示时进行调用。
/// </summary>
/// <param name="e">描述如何访问此页的事件数据。
/// 此参数通常用于配置页。</param>
protected override void OnNavigatedTo(NavigationEventArgs e)
{ }
private void Button_Click(object sender, RoutedEventArgs e)
{
//for (int i = 0; i < 20; i++)
//{
// //使用数据绑定过后不可以再使用这种形式添加数据
// list.Items.Add(DateTime.Now);
//}
var max = ViewModel.DateTimes.Count + ;
for (int i = ViewModel.DateTimes.Count - ; i < max; i++)
{
ViewModel.DateTimes.Add(string.Format("{0}\t{1}", i, DateTime.Now));
}
}
}
public class MainPageViewModel
{
//ObservableCollection可以做到数据与界面列表控件实时同步
public ObservableCollection<string> DateTimes { get; set; }
}
数据展示模版

列表控件中每一个列表项都是又一个默认展示结构的

我们可以通过设置每一个列表控件的 ItemTemplate 属性达到自定义模版的效果

数据展示模版使用

直接为特定 List 控件设置特殊的 DataTemplate

将 DataTemplate 定义到资源当中

     <Grid>
<!--<ListView
x:Name="list"
ItemsSource="{Binding DateTimes}"/>
<Button
Content="加载更多"
Click="Button_Click"/>-->
<ListView
x:Name="list"
ItemsSource="{Binding Person}">
<ListView.ItemTemplate>
<DataTemplate>
<Border BorderBrush="HotPink" BorderThickness="0,0,0,1">
<StackPanel>
<TextBlock FontSize="20">
<Run Text="Id is"/>
<Run Text="{Binding Id}"/>
</TextBlock>
<TextBlock FontSize="32" Text="{Binding Name}"/>
</StackPanel>
</Border>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
     public sealed partial class MainPage : Page
{
public MainPageViewModel ViewModel { get; set; }
public MainPage()
{
ViewModel = new MainPageViewModel();
ViewModel.DateTimes = new ObservableCollection<string>();
for (int i = ; i < ; i++)
{
ViewModel.DateTimes.Add(string.Format("{0}\t{1}", i, DateTime.Now));
}
ViewModel.Person = new ObservableCollection<Person>
{
new Person{Id=,Name="Hello"},
new Person{Id=,Name="World"},
new Person{Id=,Name="Hello World"}
};
this.InitializeComponent();
this.NavigationCacheMode = NavigationCacheMode.Required;
}
/// <summary>
/// 在此页将要在 Frame 中显示时进行调用。
/// </summary>
/// <param name="e">描述如何访问此页的事件数据。
/// 此参数通常用于配置页。</param>
protected override void OnNavigatedTo(NavigationEventArgs e)
{ }
private void Button_Click(object sender, RoutedEventArgs e)
{
//for (int i = 0; i < 20; i++)
//{
// //使用数据绑定过后不可以再使用这种形式添加数据
// list.Items.Add(DateTime.Now);
//}
var max = ViewModel.DateTimes.Count + ;
for (int i = ViewModel.DateTimes.Count - ; i < max; i++)
{
ViewModel.DateTimes.Add(string.Format("{0}\t{1}", i, DateTime.Now));
}
}
}
public class MainPageViewModel
{
//ObservableCollection可以做到数据与界面列表控件实时同步
public ObservableCollection<string> DateTimes { get; set; }
public ObservableCollection<Person> Person { get; set; }
}
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public override string ToString()
{
return string.Format("id:{0};name:{1}", Id, Name);
}
}
上一篇:【转载】C++异常机制的学习


下一篇:[BZOJ4034] [HAOI2015] T2 (树链剖分)