Windows Phone

Windows Phone 数据绑定

Jesse Liberty

下载代码示例

几乎每个有意义的 Windows Phone 应用程序有一些种类的数据,并将该数据连接 UI (视图) 中的元素的能力是绝对必要。 有多种方法可用来以编程方式执行此操作 (分配值,你去),但 XAML 编程的更加强大,最重要的功能之一是将数据绑定到控件的能力。

这里是个好消息:

  • 这不难理解。
  • 它不是很难实施。

为了说明数据绑定,您将创建中显示的网页图1。 当它加载时,它将从一个人的对象,您将创建填充。 在 UI 中的每个值将被绑定到对象中的属性的人,和实际绑定到控件的数据将自动 — — 没有 C# 代码需要。

The Data Binding View in Windows Phone
图 1 在 Windows Phone 中的数据绑定视图

Visual J# 入门

若要开始,在 Visual Studio 中创建一个新的 Windows Phone 应用程序并将其命名数据绑定。 首先创建将作为您会 (也称为 DataContext) 绑定到的数据的类。 右键单击该项目,并选择添加 |新 |类和名称的类 Person.cs。

人将包含你要在此视图中显示的 (至少) 的所有属性。 类包含枚举和一组自动属性,如中所示图 2

图 2 人类

public class Person
{
  public enum Sex
  {
    Male,
    Female,
  }
  public string Name { get; set; }
  public bool Moustache { get; set; }
  public bool Goatee { get; set; }
  public bool Beard { get; set; }
  public Sex WhichSex { get; set; }
  public double Height { get; set; }
  public DateTime BirthDate { get; set; }
  public bool Favorite { get; set; }
}

你可以看到非常快地如何这些属性将映射到所示的各种输入控件图 1。 布尔值可以是复选框或单选按钮 (具体取决于他们是否相互排斥)。

创建窗体

下一个任务是创建该窗体,您将使用数据绑定。 右键单击该项目,然后选择"在表达混合中的打开"。作为一项规则,往往会在表达混合中创建我的用户界面并在 Visual Studio 中写我的代码。

在内容的网格中,创建六行和两个列并拖动相应的输入控件。 图 3 显示 XAML 你会产生。

图 3 的 XAML 创建窗体

<Grid
  x:Name="ContentPanel"
  Grid.Row="1"
  Margin="24,0,0,0">
  <Grid.ColumnDefinitions>
    <ColumnDefinition
      Width="0.384*" />
    <ColumnDefinition
      Width="0.616*" />
  </Grid.ColumnDefinitions>
  <Grid.RowDefinitions>
    <RowDefinition
      Height="0.1*" />
    <RowDefinition
      Height="0.1*" />
    <RowDefinition
      Height="0.1*" />
    <RowDefinition
      Height="0.1*" />
    <RowDefinition
      Height="0.1*" />
    <RowDefinition
      Height="0.1*" />
    <RowDefinition
      Height="0.2*" />
  </Grid.RowDefinitions>
  <TextBlock
    x:Name="NamePrompt"
    TextWrapping="Wrap"
    Text="Name"
    Grid.Row="0"
    HorizontalAlignment="Left"
    VerticalAlignment="Center" />
  <TextBlock
    x:Name="SexPrompt"
    Grid.Row="2"
    TextWrapping="Wrap"
    HorizontalAlignment="Left"
    VerticalAlignment="Center"
    Text="Sex" />
  <TextBlock
    x:Name="HeightPrompt"
    TextWrapping="Wrap"
    Text="Height, StringFormat=F3"
    HorizontalAlignment="Left"
    Grid.Row="3"
    d:LayoutOverrides="Height"
    VerticalAlignment="Center" />
  <TextBlock
    x:Name="FavoritePrompt"
    TextWrapping="Wrap"
    Text="Favorite"
    HorizontalAlignment="Left"
    Grid.Row="4"
    d:LayoutOverrides="Height"
    VerticalAlignment="Center" />
  <TextBox
    x:Name="Name"
    TextWrapping="Wrap"
    d:LayoutOverrides="Height"
    Grid.Column="1"
    HorizontalAlignment="Left"
    Width="200"
    VerticalAlignment="Center"
    Text="{Binding Name}" />
  <StackPanel
    x:Name="BeardStackPanel"
    Grid.ColumnSpan="2"
    Grid.Row="1"
    Orientation="Horizontal">
    <CheckBox
      x:Name="Moustache"
      Content="Moustache"
      HorizontalAlignment="Left"
      VerticalAlignment="Center"
      IsChecked="{Binding Moustache}" />
    <CheckBox
      x:Name="Goatee"
      Content="Goatee"
      IsChecked="{Binding Goatee}" />
    <CheckBox
      Content="Beard"
      IsChecked="{Binding Beard}"/>
  </StackPanel>
  <StackPanel
    x:Name="SexStackPanel"
    Grid.Column="1"
    Grid.Row="2"
    Orientation="Horizontal">
    <RadioButton
      x:Name="Male"
      Content="Male"
      IsChecked="True"
      GroupName="Sex" />
    <RadioButton
      x:Name="Female"
      Content="Female"
      GroupName="Sex" />
  </StackPanel>
  <StackPanel
    x:Name="HeightStackPanel"
    Grid.Column="1"
    Grid.Row="3"
    Orientation="Horizontal">
    <TextBlock
      TextWrapping="Wrap"
      Text="{Binding Height}"
      VerticalAlignment="Center"
      HorizontalAlignment="Left"
      Margin="0,0,0,0" />
    <TextBlock
      VerticalAlignment="Center"
      HorizontalAlignment="Left"
      Margin="5,0,0,0"
      Text="meters" />
  </StackPanel>
  <ToggleButton
    x:Name="Favorite"
    Content="Favorite"
    Grid.Column="1"
    Grid.Row="4"
    d:LayoutOverrides="Width, Height"
    HorizontalAlignment="Left"
    VerticalAlignment="Center"
    IsChecked="{Binding Favorite}" />
</Grid>

Binding

现在,每个文本输入字段已使用绑定语法设置其值。 例如,说要绑定文本框,标识,其属性将需要数据 — — 在本例中,文本属性 — — 和使用绑定语法中,较早前所示。

绑定,大括号内,他们使用关键字通常具有约束力,紧接着约束力属性的属性的名称。 例如,此 XAML 声明将获得从一个名为名称的公共属性的文本框中的文本:

<TextBox
  x:Name="Name"
  TextWrapping="Wrap"
  d:LayoutOverrides="Height"
  Grid.Column="1"
  HorizontalAlignment="Left"
  Width="200"
  VerticalAlignment="Center"
  Text="{Binding Name}" />

同样地,用脸上的毛发的复选框,IsChecked 属性绑定到适当的属性:

<CheckBox
  x:Name="Moustache"
  Content="Moustache"
  HorizontalAlignment="Left"
  VerticalAlignment="Center"
  IsChecked="{Binding Moustache}" />

你还不知道什么对象将具有这些属性 (名称和胡子)。 正如前文所述,包含可绑定的属性的对象称为 DataContext。 它可以是任何东西,但这种情况下你要创建类的实例的人,然后你要设置该人对象是整个视图的 DataContext。

请注意您可以为一个容器,在本例中,页面,设置 DataContext 该容器中的所有视图控件将都共享该 DataContext — — 虽然你可以自由地将其他数据分配­一个或多个单独的控件的上下文。

您可以实例化的代码隐藏中页的加载的事件处理程序中的人。 加载的事件被称为一旦加载页和控件进行初始化,如图所示,在图 4

图 4 在加载页面时,将调用加载的事件

private Person _currentPerson;
private Random _rand = new Random();
public MainPage()
{
  InitializeComponent();
  Loaded += MainPage_Loaded;
}
void MainPage_Loaded( object sender, RoutedEventArgs e )
{
  _currentPerson = new Person
  {
    Beard = false,
    Favorite = true,
    Goatee = false,
    Height = 1.86,
    Moustache = true,
    Name = "Jesse",
    WhichSex = Person.Sex.Male
  };
}

现在,您可以设置每个控件的 DataContext 在您只是实例化 (在加载的事件处理程序) 的 _currentPerson 对象的 ContentPanel:

ContentPanel.DataContext = _currentPerson;

一旦知悉其 DataContext,文本框可以解决名称属性,并获得值 ("杰西") 来显示。 对于所有其他控件,也是如此每个绑定到新的人对象中的属性。

运行应用程序,您应该看到适当地绑定的所有字段。

更改 DataContext

若要开车回家绑定和显示之间的关系,让我们创建的对象的人的数量,并显示一个接一个。 为此,请修改 MainPage.xaml.cs 创建 (随机创建) 的人的名单,然后循环访问列表的用户界面,您应该将其添加到底部行上新的"下一步"按钮:

<Button
  Name="Next"
  Content="Next"
  Grid.Row="5"
  HorizontalAlignment="Center"
  VerticalAlignment="Center" />

下面是修改后的代码与下一步按钮进行交互:

void MainPage_Loaded( object sender, RoutedEventArgs e )
{
  SetDataContext();
  Next.Click += Next_Click;
}
private void SetDataContext()
{
  ContentPanel.DataContext = GeneratePerson();
}
void Next_Click( object sender, RoutedEventArgs e )
{
  SetDataContext();
}

请注意页面加载事件和下一步按钮的 click 事件处理程序必须设置 DataContext,所以我已经考虑了,一个单独的方法:SetDataContext。 这种方法,反过来,调用 GeneratePerson 方法,他的工作是随机创建一个人。

现在,您可以进行所有更改的代码隐藏中。 首先,停止当前的人,hardwiring,而通过调用 GeneratePerson 方法对它进行设置。

生成一个随机的人

这里是整个的 GeneratePerson 方法 ; 你会看到我已经计入调用 FlipCoin 方法选择真与假的任务:

private Person GeneratePerson()
{
  var newPerson = new Person
  {
    Beard = FlipCoin(),
    Favorite = FlipCoin(),
    Goatee = FlipCoin(),
    Height = _rand.NextDouble() + 1,
    Moustache = FlipCoin(),
    Name = names[_rand.Next(0, names.Count - 1)]
  };
  return newPerson;
}

FlipCoin 使用随机数生成器返回 true 50%的时间:

private  bool FlipCoin()
{
  return _rand.Next( 1, 3 ) % 2 == 0;
}

最后,要选择一个名称,创建可以分配给男子或妇女,并使用随机数生成器捡到列表中的一个偏移量的六个名称的列表:

private readonly List<string> names = new List<string>()
{
  "Stacey",
  "Robbie",
  "Jess",
  "Robin",
  "Syd",
  "J.J.",
  "Terri",
  "Moonunit",
};

运行应用程序,然后单击下一步按钮。 人的每个对象创建时设置为 DataContext 和其属性绑定到用户界面控件。

INotifyPropertyChanged

如果您人对象上的属性之一发生变化,将会发生什么? 这可能很容易发生如果包含对象的数据库中,其他用户可以访问相同的对象。 您希望您的 UI 进行更新。

为此,您的类 (DataContext) 必须实现 INotifyPropertyChanged — — 一个简单的界面,允许每个属性,其值更改时通知用户界面。 它是共同创建检查以确保该事件具有至少一个已注册的方法的帮助器方法。 如果是这样,帮助器方法将引发的事件,经过更新的属性的名称。

若要看到这在工作,请添加一个新按钮命名变化的用户界面。 当您单击更改按钮时,更改为"雅"的名称属性:

void Change_Click( object sender, RoutedEventArgs e )
{
  _currentPerson.Name = "Jacob";
}

这不会影响除非人实现 INotifyPropertyChanged 和名称属性将引发 PropertyChanged 事件,如图所示,在图 5

图 5 INotifyPropertyChanged 接口

public class Person : INotifyPropertyChanged
{
  public string _name;
  public string Name
  {
    get { return _name; }
    set
    {     
      _name = value;
      PropChanged( "Name" );
    }
  }
// Other properties
  public event PropertyChangedEventHandler PropertyChanged;
  private void PropChanged(string propName)
  {
    if (PropertyChanged != null)
    {
      PropertyChanged( this, new PropertyChangedEventArgs( propName ) );
    }
  }
}

这地方,当您单击更改按钮在用户界面中显示的名称将更改为新名称的对象 (雅) 中。

双向绑定

如果,用户与用户界面交互并直接更改一个值 (例如,到名称文本框类型的新名称) 吗? (通常情况下),您需要更改,推到基础数据 (DataContext 对象)。 为此,您将使用双向绑定。

要修改程序名称属性上使用双向绑定,查找名称绑定并对此进行修改:

<TextBox
  x:Name="Name"
  TextWrapping="Wrap"
  d:LayoutOverrides="Height"
  Grid.Column="1"
  HorizontalAlignment="Left"
  Width="200"
  VerticalAlignment="Center"
  Text="{Binding Name, Mode=TwoWay}" />

有约束力的三种模式:

  1. 一次性绑定意味着数据是绑定,但永远不会再更新,即使用户更新数据。
  2. 单向绑定是默认值 ; 数据是从源拉到用户界面,但不是推入源。
  3. 双向绑定允许来自源拉和推到源,如果在用户界面中修改数据。

元素绑定

到六行更改为下一步按钮的行并拖动滑块控件到行五。 有很多非常重要的滑块,设置包括:

  • 最低要求
  • Maximum
  • 价值
  • SmallChange

您可以看到这些代码中反映图 6

图 6 添加滑块

<Slider
  x:Name="Likability"
  Grid.Row="5"
  Grid.Column="0"
  BorderBrush="White"
  BorderThickness="1"
  Background="White"
  Foreground="Blue"
  LargeChange="10"
  SmallChange="1"
  Minimum="0"
  Width="199"
  Maximum="100"
  Value="50"
  Height="90" />

最小值和最大值设置滑块的范围。 在此情况下,我用百分比,因为我已经他们分别设置为 0 和 100。

值是当前值的滑块,并将会:最低 < = 值 < = 最大值

都和 SmallChange 用于像它们在滚动条 ; 它们分别表示什么单击滑块到会做、 会做什么使用另一个控件 (也许箭头) 以增量方式移动滑块。

设置 2

在右侧的列中,您将使用三个 TextBlocks ; 第一和第三,作为固定的标签 (值"讲师:"和"%,"分别)。 中间 TextBlock 显示滑块的值的数字表示。

要实现此目的,中间 TextBlock 的文本值属性绑定到滑块,识别您正在用关键字 ElementName,绑定到哪个元素中所示的值属性图 7

图 7 TextBlock 的 Value 属性绑定

<StackPanel
  x:Name="LikeabilityPercentStackPanel"
  Grid.Row="5"
  Grid.Column="1"
  Orientation="Horizontal">
  <TextBlock
    Text="Likeability: "
    HorizontalAlignment="Left"
    VerticalAlignment="Center"
    Margin="20,0,5,0" />
  <TextBlock
    x:Name="SliderValue"
    Text="{Binding Value, ElementName=Likeability, StringFormat=F3}"
    HorizontalAlignment="Left"
    VerticalAlignment="Center"
    Margin="5,0,0,0"/>
  <TextBlock
    Text="%"
    HorizontalAlignment="Left"
    VerticalAlignment="Center" />
</StackPanel>

运行该程序。 滑块,调整 TextBlock 中的值将立即更新。

数据转换器

某些属性不会正确绑定到给定的用户界面控件,或者您可能希望更好地控制如何显示的值。 作为一个简单的示例,让我们通过下移一行移动按钮和插入行 ("出生日期") 的提示和人的出生日期的值显示在用户的出生日期。

为此,您必须修改中生成有效的生日、 你通过添加创建随机的出生日期在过去这行做 MainPage.xaml.cs 的 GeneratePerson 方法 20 年:

BirthDate = DateTime.Now - TimeSpan.FromDays(_rand.Next(1,365*20)),

如果你只是将绑定到出生日期属性,您将看到的出生日期和时间。 但是您不希望时间 — — 只是日期的短日期格式。 要完成此任务,您需要 DataConverter。

DataConverters 是 IValueConverter 实现的类。 此接口要求中所示的两个方法, 图 8

图 8 IValueConverter 接口

public object Convert(
  object value,
  Type targetType,
  object parameter,
  System.Globalization.CultureInfo culture )
{
  throw new NotImplementedException();
}
public object ConvertBack(
  object value,
  Type targetType,
  object parameter,
  System.Globalization.CultureInfo culture )
{
  throw new NotImplementedException();
}

在此情况下,您只需 (永远不会称为第二) 的两个第一。 该方法是相当简单的实施 ; 请确保目标类型是字符串的值类型是日期时间。 如果是这样,采取值强制转换为日期时间,然后调用它,作为放映中的 ToShortDateString 图 9

图 9,缩短日期时间转换方法

public object Convert(
  object value,
  Type targetType,
  object parameter,
  System.Globalization.CultureInfo culture )
{
  if (targetType == typeof( string ) &&
    value.GetType() == typeof( DateTime ))
  {
    return (( DateTime ) value).ToShortDateString();
  }
  else  // Unable to convert
  {
    return value;
   }
}
public object ConvertBack(
  object value,
  Type targetType,
  object parameter,
  System.Globalization.CultureInfo culture )
{
  throw new NotImplementedException();
}

这个地方,你需要一种方式来访问值转换器 ; XAML 可以通过使转换器资源完成此操作。 打开 App.xaml 并添加您的转换器,基于您的应用程序的命名空间的命名空间:

xmlns:mine="clr-namespace:DataBinding">

下一步,在相同的文件中,找到 <Application.Resource> 部分并添加您的值转换器的资源:

<Application.Resources>
  <mine:DateConverter   x:Key="dateConverter" />
</Application.Resources>

您现在可以使用 XAML 文件中的关键。 更新的绑定使用资源的出生日期:

<TextBlock
  Grid.Row="6"
  Grid.Column="1"
  VerticalAlignment="Center"
  Text="{Binding BirthDate, Converter={StaticResource dateConverter}}" />

运行该程序,您应该会看到显示短日期格式的日期。

功能强大的应用程序

数据绑定允许您创建功能强大的 Windows Phone 应用程序可靠地管理基础数据和控件和视图,显示该数据之间的关系。 在本文中,您看到了如何创建简单的数据绑定和双向数据绑定、 如何将绑定到元素和如何使用数据转换器来按摩到所需的格式的数据。

Jesse Liberty 是 Windows Phone 团队的高级开发人员-社区推广专家。自由承载流行尚未另一个的播客 (jesseliberty.com/podcast),和他的博客 (jesseliberty.com) 是必读。他是众多最畅销的书,包括"编程无功扩展和 LINQ"(很,2011年) 和"迁移到 Windows Phone"(很,2011年) 的作者。请在 Twitter 上关注 Liberty:twitter.com/JesseLiberty

多亏了以下的技术专家审查这篇文章:Jason Shaver