1 <Grid> 2 <TextBox 3 x:Name="txtHello" 4 Text="{Binding}" 5 Header="{Binding Path=Value}"/> 6 <Button 7 x:Name="btnHello" 8 Content="按钮" 9 Click="btnHello_Click"/> 10 </Grid>
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 public class MainPageData 2 { 3 public string Value { get; set; } 4 }
1 public MainPageData Data { get; set; } 2 private void btnHello_Click(object sender, RoutedEventArgs e) 3 { 4 Data = new MainPageData(); 5 Data.Value = DateTime.Now.ToString(); 6 //当前页面对象,数据上下文可以被“继承” 7 this.DataContext = Data; 8 }
通过前台进行数据绑定
1 <Page 2 x:Class="Myapp.MainPage" 3 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 4 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 5 xmlns:local="using:Myapp" 6 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 7 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 8 mc:Ignorable="d" 9 10 DataContext="{Binding Data,RelativeSource={RelativeSource Self}}" 11 12 Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> 13 <StackPanel> 14 <TextBox Text="{Binding Text1}"/> 15 <TextBox Text="{Binding Text2}"/> 16 </StackPanel> 17 </Page>
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 public class DataBindingPageData 2 { 3 public string Text1 { get; set; } 4 public string Text2 { get; set; } 5 }
1 public sealed partial class MainPage : Page 2 { 3 //当前页面数据源 4 public DataBindingPageData Data { get; set; } 5 public MainPage() 6 { 7 //该操作必须在页面初始化之前 8 Data = new DataBindingPageData(); 9 Data.Text1 = "Hello"; 10 Data.Text2 = "World"; 11 //界面元素初始化操作 12 this.InitializeComponent(); 13 this.NavigationCacheMode = NavigationCacheMode.Required; 14 }
1 <Page 2 x:Class="Myapp.MainPage" 3 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 4 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 5 xmlns:local="using:Myapp" 6 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 7 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 8 mc:Ignorable="d" 9 10 DataContext="{Binding Data,RelativeSource={RelativeSource Self}}" 11 12 Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> 13 <StackPanel> 14 <TextBox Text="{Binding Text1.Value}"/> 15 <TextBox Text="{Binding Text2}"/> 16 17 <TextBox x:Name="tb1"/> 18 </StackPanel> 19 </Page>
1 namespace MyApp 2 { 3 4 /// <summary> 5 /// 可用于自身或导航至 Frame 内部的空白页。 6 /// </summary> 7 public sealed partial class MainPage : Page 8 { 9 //当前页面数据源 10 public DataBindingPageData Data { get; set; } 11 public MainPage() 12 { 13 //该操作必须在页面初始化之前 14 Data = new DataBindingPageData(); 15 Data.Text1 = new SubData { Value = "Hello" }; 16 Data.Text2 = "World"; 17 //界面元素初始化操作 18 this.InitializeComponent(); 19 this.NavigationCacheMode = NavigationCacheMode.Required; 20 } 21 /// <summary> 22 /// 在此页将要在 Frame 中显示时进行调用。 23 /// </summary> 24 /// <param name="e">描述如何访问此页的事件数据。 25 /// 此参数通常用于配置页。</param> 26 protected override void OnNavigatedTo(NavigationEventArgs e) 27 { 28 #region 数据绑定四个组成部分(目标.依赖属性{绑定}数据源.数据源属性) 29 //绑定桥梁(两头分别是谁) 30 Binding binding = new Binding(); 31 //指定数据源和数据源属性 32 binding.Source = Data; 33 binding.Path = new PropertyPath("Text2"); 34 //指定绑定的目标依赖属性 35 tb1.SetBinding(TextBox.TextProperty, binding); 36 #endregion 37 } 38 } 39 public class DataBindingPageData 40 { 41 public SubData Text1 { get; set; } 42 public string Text2 { get; set; } 43 } 44 public class SubData 45 { 46 public string Value { get; set; } 47 } 48 }
1 <Page.Resources> 2 <SolidColorBrush x:Key="color" Color="AliceBlue"/> 3 <!--资源中定义数据源对象--> 4 <local:ResourceBindingPageData 5 x:Key="myData" 6 Value="Hello World"/> 7 </Page.Resources> 8 <Grid DataContext="{StaticResource myData}"> 9 <TextBox Text="{Binding Value}"/> 10 </Grid>
1 public class ResourceBindingPageData 2 { 3 public string Value { get; set; } 4 }
数据绑定的数据源不仅仅可以是一个自定义对象,还可以使用 UI 元素对象
就是将一个界面元素对象作为绑定的数据源,轻松实现两个控件之间的交互
基本语法:
Width="{Binding ElementName=sliderDiameter, Path=Value}"
ElementName 就是指定数据源控件的 Name 属性
1 <StackPanel> 2 <Slider 3 x:Name="slider" 4 Minimum="50" 5 Maximum="300"/> 6 <Ellipse 7 x:Name="ellipse" 8 Width="{Binding ElementName=slider,Path=Value}" 9 Height="{Binding ElementName=slider,Path=Value}" 10 Fill="HotPink"/> 11 </StackPanel>
当数据源和目标属性类型或格式不相同时,比如URL链接,可以自定义两者之间的转换方式
具体就是实现 IValueConverter 接口
1 <Page.Resources> 2 <local:BookIdToBookUrlConverter x:Key="BookIdToBookUrlConverter"/> 3 </Page.Resources> 4 <Grid> 5 <TextBox Text="{Binding BookId,Converter={StaticResource BookIdToBookUrlConverter}}"/> 6 </Grid>
1 namespace MyApp 2 { 3 /// <summary> 4 /// 可用于自身或导航至 Frame 内部的空白页。 5 /// </summary> 6 public sealed partial class MainPage : Page 7 { 8 public ValueConverterPegaData Data { get; set; } 9 public MainPage() 10 { 11 Data = new ValueConverterPegaData { BookId = 10 }; 12 //Data.BookUrl = string.Format("http://www.itcast.cn/book/{0}", Data.BookId); 13 this.InitializeComponent(); 14 this.NavigationCacheMode = NavigationCacheMode.Required; 15 this.DataContext = Data; 16 } 17 18 /// <summary> 19 /// 在此页将要在 Frame 中显示时进行调用。 20 /// </summary> 21 /// <param name="e">描述如何访问此页的事件数据。 22 /// 此参数通常用于配置页。</param> 23 protected override void OnNavigatedTo(NavigationEventArgs e) 24 { 25 // TODO: 准备此处显示的页面。 26 27 // TODO: 如果您的应用程序包含多个页面,请确保 28 // 通过注册以下事件来处理硬件“后退”按钮: 29 // Windows.Phone.UI.Input.HardwareButtons.BackPressed 事件。 30 // 如果使用由某些模板提供的 NavigationHelper, 31 // 则系统会为您处理该事件。 32 } 33 } 34 public class ValueConverterPegaData 35 { 36 public int BookId { get; set; } 37 public string BookUrl { get; set; } 38 } 39 public class BookIdToBookUrlConverter : IValueConverter 40 { 41 //从原数据转换成目标数据 42 public object Convert(object value, Type targetType, object parameter, string language) 43 { 44 return string.Format("http://www.itcast.cn/book/{0}", value); 45 } 46 //从目标数据转换成原数据 47 public object ConvertBack(object value, Type targetType, object parameter, string language) 48 { 49 return null; 50 } 51 } 52 }
IValueConverter接口实现数值类型转换
1 <StackPanel> 2 <StackPanel.Resources> 3 <local:ValueToColorConverter x:Key="ValueToColorConverter"/> 4 </StackPanel.Resources> 5 <Slider 6 x:Name="slider" 7 Minimum="0" 8 Maximum="255"/> 9 <Ellipse 10 x:Name="ellipse" 11 Width="{Binding ElementName=slider,Path=Value}" 12 Height="{Binding ElementName=slider,Path=Value}" 13 Fill="{Binding ElementName=slider,Path=Value,Converter={StaticResource ValueToColorConverter}}"/> 14 </StackPanel>
1 public class ValueToColorConverter : IValueConverter 2 { 3 public object Convert(object value, Type targetType, object parameter, string language) 4 { 5 var d = (double)value; 6 var b = (byte)d; 7 return new SolidColorBrush(Color.FromArgb(255, b, b, b)); 8 } 9 public object ConvertBack(object value, Type targetType, object parameter, string language) 10 { 11 return null; 12 } 13 }
OneTime:一次绑定,即数据上下文初始化时更新目标属性
OneWay:单向绑定,仅当数据源属性发生变更时更新目标属性
TwoWay:双向绑定,当目标属性发生变更通知数据源变更(同步)
1 <Grid x:Name="LayoutRoot"> 2 3 <Grid.RowDefinitions> 4 <RowDefinition Height="Auto"/> 5 <RowDefinition Height="*"/> 6 </Grid.RowDefinitions> 7 8 <!-- 标题面板 --> 9 <StackPanel Grid.Row="0" Margin="19,0,0,0"> 10 <TextBlock Text="MY APPLICATION" Style="{ThemeResource TitleTextBlockStyle}" Margin="0,12,0,0"/> 11 <TextBlock 12 Text="绑定模式" 13 Margin="0,-6.5,0,26.5" 14 Style="{ThemeResource HeaderTextBlockStyle}" 15 CharacterSpacing="{ThemeResource PivotHeaderItemCharacterSpacing}"/> 16 </StackPanel> 17 18 <!--TODO: 应将内容放入以下网格--> 19 <Grid Grid.Row="1" x:Name="ContentRoot" Margin="19,9.5,19,0"> 20 <Grid.RowDefinitions> 21 <RowDefinition Height="auto"/> 22 <RowDefinition/> 23 <RowDefinition/> 24 <RowDefinition/> 25 <RowDefinition/> 26 </Grid.RowDefinitions> 27 28 <Slider 29 x:Name="slider" 30 Grid.Row="0" 31 Value="10"/> 32 33 <!--默认形式是OneWay--> 34 <TextBox 35 Grid.Row="1" 36 Header="Default" 37 Text="{Binding ElementName=slider, Path=Value}"/> 38 39 <!--OneTime是在进行数据绑定时同步一次--> 40 <TextBox 41 Grid.Row="2" 42 Header="One Time" 43 Text="{Binding ElementName=slider, Path=Value, Mode=OneTime}"/> 44 45 <!--OneWay会实时同步数据源变化,但是不会将自身变化同步到数据源--> 46 <TextBox 47 Grid.Row="3" 48 Header="One Way" 49 Text="{Binding ElementName=slider, Path=Value, Mode=OneWay}"/> 50 51 <!--OneWay会实时同步数据源变化,会将自身变化同步到数据源--> 52 <TextBox 53 Grid.Row="4" 54 Header="Two Way" 55 Text="{Binding ElementName=slider, Path=Value, Mode=TwoWay}"/> 56 57 </Grid> 58 </Grid>
数据变更通知(一)
使用自定义数据源进行数据绑定时,不管绑定模式是哪一种,都不会造成目标对象实时同步
如果想要实现数据变更实时同步,就必须要让自定义数据类型实现 INotifyPropertyChanged 接口
INotifyPropertyChanged 具有 PropertyChanged 的事件,在数据源被绑定时注册,数据源变更时被触发(需要自己写代码手动触发)
TwoWay 绑定会在目标和源中的任一个发生更改时同时更新目标和源
此行为的一个例外是,在每次用户击键之后,不会将 TextBox.Text 的更改发送给绑定源
除非将 Binding.UpdateSourceTrigger 设置为 PropertyChanged
默认情况下,仅当 TextBox 失去焦点时才发送更改
INotifyPropertyChanged 封装
1 <StackPanel> 2 <TextBlock/> 3 <TextBox/> 4 <Slider/> 5 </StackPanel>
1 public class NotifyPropertyChangedPageViewModel : ViewModelBase 2 { 3 public string Text1 { get; set; } 4 private string text2; 5 public string Text2 6 { 7 get { return text2; } 8 set 9 { 10 SetProperty(ref text2, value); 11 } 12 } 13 public double number; 14 public double Number 15 { 16 get { return number; } 17 set 18 { 19 SetProperty(ref number, value); 20 } 21 } 22 private string hello; 23 public string Hello 24 { 25 get { return hello; } 26 set 27 { 28 SetProperty(ref hello, value); 29 } 30 } 31 } 32 public abstract class ViewModelBase : NotifyPropertyChanged 33 { 34 protected void SetProperty<TProperty>(ref TProperty current, TProperty value, [CallerMemberName]string propertyName = null) 35 { 36 if (object.Equals(current, value)) 37 { 38 //属性值没有发生变化,减轻应用程序负担 39 return; 40 } 41 current = value; 42 OnPropertyChanged(propertyName); 43 } 44 } 45 public abstract class NotifyPropertyChanged : INotifyPropertyChanged 46 { 47 /// <summary> 48 /// 当属性发生变化时调用 49 /// </summary> 50 /// <param name="propertyName"></param> 51 protected void OnPropertyChanged(string propertyName) 52 { 53 if (PropertyChanged != null && !string.IsNullOrEmpty(propertyName)) 54 { 55 PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); 56 } 57 } 58 public event PropertyChangedEventHandler PropertyChanged; 59 }
列表控件主要是 ListBox、ListView、GridView 等
为列表控件绑定数据不再是为 DataContext 属性赋值,应该使用列表控件自有的 ItemsSource 属性
当列表数据元素由 ItemsSource 绑定,就不能再动态操作(增删改) Items 属性
绑定集合元素和普通绑定一样,界面显示默认不会跟随数据源变化而变化
如果要实现一个动态绑定的列表,通常我们需要用到 ObservableCollection<T> 类型作为数据源集合的类型
1 <Grid> 2 <ListView 3 x:Name="list" 4 ItemsSource="{Binding DateTimes}"/> 5 <Button 6 Content="加载更多" 7 Click="Button_Click"/> 8 </Grid>
1 public sealed partial class MainPage : Page 2 { 3 public MainPageViewModel ViewModel { get; set; } 4 public MainPage() 5 { 6 ViewModel = new MainPageViewModel(); 7 ViewModel.DateTimes = new ObservableCollection<string>(); 8 for (int i = 0; i < 30; i++) 9 { 10 ViewModel.DateTimes.Add(string.Format("{0}\t{1}", i, DateTime.Now)); 11 } 12 this.InitializeComponent(); 13 this.NavigationCacheMode = NavigationCacheMode.Required; 14 } 15 /// <summary> 16 /// 在此页将要在 Frame 中显示时进行调用。 17 /// </summary> 18 /// <param name="e">描述如何访问此页的事件数据。 19 /// 此参数通常用于配置页。</param> 20 protected override void OnNavigatedTo(NavigationEventArgs e) 21 { 22 23 } 24 private void Button_Click(object sender, RoutedEventArgs e) 25 { 26 //for (int i = 0; i < 20; i++) 27 //{ 28 // //使用数据绑定过后不可以再使用这种形式添加数据 29 // list.Items.Add(DateTime.Now); 30 //} 31 var max = ViewModel.DateTimes.Count + 20; 32 for (int i = ViewModel.DateTimes.Count - 1; i < max; i++) 33 { 34 ViewModel.DateTimes.Add(string.Format("{0}\t{1}", i, DateTime.Now)); 35 } 36 } 37 } 38 public class MainPageViewModel 39 { 40 //ObservableCollection可以做到数据与界面列表控件实时同步 41 public ObservableCollection<string> DateTimes { get; set; } 42 }
列表控件中每一个列表项都是又一个默认展示结构的
我们可以通过设置每一个列表控件的 ItemTemplate 属性达到自定义模版的效果
直接为特定 List 控件设置特殊的 DataTemplate
将 DataTemplate 定义到资源当中
1 <Grid> 2 <!--<ListView 3 x:Name="list" 4 ItemsSource="{Binding DateTimes}"/> 5 <Button 6 Content="加载更多" 7 Click="Button_Click"/>--> 8 <ListView 9 x:Name="list" 10 ItemsSource="{Binding Person}"> 11 <ListView.ItemTemplate> 12 <DataTemplate> 13 <Border BorderBrush="HotPink" BorderThickness="0,0,0,1"> 14 <StackPanel> 15 <TextBlock FontSize="20"> 16 <Run Text="Id is"/> 17 <Run Text="{Binding Id}"/> 18 </TextBlock> 19 <TextBlock FontSize="32" Text="{Binding Name}"/> 20 </StackPanel> 21 </Border> 22 </DataTemplate> 23 </ListView.ItemTemplate> 24 </ListView> 25 </Grid>
1 public sealed partial class MainPage : Page 2 { 3 public MainPageViewModel ViewModel { get; set; } 4 public MainPage() 5 { 6 ViewModel = new MainPageViewModel(); 7 ViewModel.DateTimes = new ObservableCollection<string>(); 8 for (int i = 0; i < 30; i++) 9 { 10 ViewModel.DateTimes.Add(string.Format("{0}\t{1}", i, DateTime.Now)); 11 } 12 ViewModel.Person = new ObservableCollection<Person> 13 { 14 new Person{Id=1,Name="Hello"}, 15 new Person{Id=2,Name="World"}, 16 new Person{Id=3,Name="Hello World"} 17 }; 18 this.InitializeComponent(); 19 this.NavigationCacheMode = NavigationCacheMode.Required; 20 } 21 /// <summary> 22 /// 在此页将要在 Frame 中显示时进行调用。 23 /// </summary> 24 /// <param name="e">描述如何访问此页的事件数据。 25 /// 此参数通常用于配置页。</param> 26 protected override void OnNavigatedTo(NavigationEventArgs e) 27 { 28 29 } 30 private void Button_Click(object sender, RoutedEventArgs e) 31 { 32 //for (int i = 0; i < 20; i++) 33 //{ 34 // //使用数据绑定过后不可以再使用这种形式添加数据 35 // list.Items.Add(DateTime.Now); 36 //} 37 var max = ViewModel.DateTimes.Count + 20; 38 for (int i = ViewModel.DateTimes.Count - 1; i < max; i++) 39 { 40 ViewModel.DateTimes.Add(string.Format("{0}\t{1}", i, DateTime.Now)); 41 } 42 } 43 } 44 public class MainPageViewModel 45 { 46 //ObservableCollection可以做到数据与界面列表控件实时同步 47 public ObservableCollection<string> DateTimes { get; set; } 48 public ObservableCollection<Person> Person { get; set; } 49 } 50 public class Person 51 { 52 public int Id { get; set; } 53 public string Name { get; set; } 54 public override string ToString() 55 { 56 return string.Format("id:{0};name:{1}", Id, Name); 57 } 58 }