65.9K
CodeProject 正在变化。 阅读更多。
Home

i2W:iPhone 开发人员的首个 Windows Phone 7 应用程序

starIconstarIconstarIconstarIconstarIcon

5.00/5 (3投票s)

2010年12月14日

CPOL

16分钟阅读

viewsIcon

22988

在本教程中,我们将构建一个有意义的、非平凡的Windows Phone 7应用程序。这将使我们有机会探索布局控件、交互式控件、Visual Studio 以及更多内容。

最初发布在 Jesse Liberty 的博客上

form_thumb.jpg

让我们先来看看我们要构建的应用程序。

该应用程序以一个表单打开,该表单由数据绑定填充

Visual Studio, Expression Blend, XCode and Interface Builder

很容易将Visual Studio 与 XCode 映射,将Expression Blend 与 Interface Builder 映射。这并非完全错误,只是不完整,因为您可以在 Visual Studio 中创建整个界面(许多人也是这样做的),但对于更高级的 UI 开发,Blend 是首选工具。我们可以从 Blend 中创建表单本身(有些人会争辩说我们应该这样做),但为了保持简单,我们现在将在 Visual Studio 中完成所有工作,并在后续教程中介绍 Blend。

解决方案、项目和文件,哦我的天哪

MVVMForPhone_thumb.jpg

首先,打开 Visual Studio,点击“新建项目”,然后在左侧窗格(模板)中选择“Visual C#”,然后选择“Silverlight for Windows Phone 7”。在右侧,选择 MVVM Light Toolkit。给 Visual Studio 2010 几分钟时间稳定下来,您会发现 Visual Studio 2010 和 MVVM Light Toolkit 已协同创建了一个包含众多文件和文件夹的项目。您可以在“解决方案资源管理器”窗口中看到它们,该窗口通常位于开发环境的左上角,您可以从菜单(视图->解决方案资源管理器)或菜单中显示的快捷键打开它。

因为所有这些窗口都可以从菜单中打开,所以我将避免逐一介绍,而只指出那些可能令人困惑的菜单选项。

目前,我们将忽略“属性”和“引用”项(但您随时可以自行探索,带上指南针和足够的绳索,您就没事了)。

现在我们关心的关键文件是 MainPage.xaml 以及“ViewModel”文件夹下的 MainViewModel.cs。前者 MainPage.xaml 有一个关联的代码隐藏文件,您可以通过非常熟悉的向下箭头暴露它。

双击 MainViewModel.cs 以填充编辑窗口。您最初看到的将是分屏视图,设计视图在一侧,代码视图在另一侧。在这种情况下,代码视图将填充相当多的 Xaml。我们暂时关闭 Xaml 窗口,专注于设计视图。

Visual Studio 快速之旅

要关闭代码视图,请确保设计视图在左侧(您可以通过窗格之间的双箭头按钮 DoubleArrow 进行切换),然后点击双箭头 DoubleArrowHead 来关闭代码视图。最后,请确保属性窗口(F4)可见在右侧,工具箱(Ctrl-Alt-X)已固定在左侧。在下图的插图中,我标记了 7 个区域。让我们逐一介绍。

VisualStudio_thumb.jpg

区域 1 是工具箱。请注意,右上角(已圈出)有三个符号。第一个允许您

区域 2 是应用程序字符串,区域 3 是页面标题字符串;我们稍后会回到这里。

区域 4 是 ContentPanel,我们将在那里进行大部分设计工作。

区域 5 是解决方案资源管理器,按照惯例位于属性窗口的上方(当然,您可以将任何窗口移动到任何位置)。

属性窗口分为区域 6,在此设置选定对象的名称,并可以在属性视图和事件视图之间切换属性窗口(有关事件的更多内容将在本教程后面介绍)。区域 7 是设置属性的地方(您可以在此插图中看到一些已公开的 Text 属性)。

请注意从设计面板发出的箭头。上面的两个箭头用于在其中一个窗口隐藏时在代码和设计之间切换。下面的三个箭头分别垂直或水平分割窗口,并分别显示(或隐藏)右侧(或下方)的窗口。

OnlineHelp_thumb.jpg

所有这些之上是菜单,当然最重要的菜单项是“帮助”,它提供了多种接收帮助的方式。要查看其最强大的功能之一,请将光标放在 RowDefinition 关键字(大约第 47 行)的任意位置,然后按 F1。第一次按 F1 时,系统会询问您是想使用本地帮助还是在线帮助。如果您有互联网连接,请务必请求在线帮助。浏览器窗口将打开并显示有关 RowDefinition 关键字的帮助。太棒了。

截至本文撰写之时,在线帮助文件为每个支持的语言都有选项卡。我们将使用 C# 选项卡(这将为整个页面设置 C# 选项卡,并且是固定的,因此在下次打开窗口时仍然有效)。

每个条目都遵循识别类、命名空间和程序集(我们将在进行过程中介绍这两者)的模式。语法通过语言演示,*备注*部分提供详细信息和相关主题链接。

示例部分在备注部分之后,提供了可复制的代码供您开始。

示例之后是继承层次结构,显示您的类所属的位置,以及关于线程安全性、平台、版本和最后“请参阅”部分(用于相关主题)的说明。

您还可以在本地获得许多这些信息,即 Silverlight.chm – 安装 Silverlight 时加载的 Windows 帮助文件。

创建表单

让我们回到项目,开始创建表单。目前我们将在设计视图中完成所有工作,尽管稍后您会看到在设计视图中所做的一切都会立即更新 Xaml 视图,反之亦然。

布局控件

通常会区分用于布局的控件和用于与用户交互的控件,尽管这种区分实际上是任意的。无论如何,有很多布局控件,尽管我们最关心的两个是 Grid 和 StackPanel

Grid 是最强大(近乎)无处不在的布局控件——有点像是功能增强版的 UITableView。点击 Content Panel(上方区域 4),为您放置在那里的 Grid 的边缘会亮起。将光标移到空白处,线条就会出现。单击后,水平线将成为行,垂直线将成为列。您需要创建两列(左侧占据网格约 1/3)和 8 行大小大致相等的行。

点击 Grid,注意属性窗口有一个 ColumnDefinitions 属性和一个 RowDefinitions 属性。在这里,您可以使间距更精确。您可以使用精确的大小(例如,25px)或百分比,或相对大小(例如,2* 和 3* 将创建两列,第一列占据可用空间的 2/5)。我为列选择了 1*(可以缩短为 *)和 2*,为行选择了七个 1*。

当然,您也可以在设计器中打开这些,而不是先创建行和列。

设置应用程序名称和页面标题

点击应用程序标题或页面标题(上方区域 2 和 3)。

PropertiesForFirstTextBlock_thumb.jpg

我们将使用左侧列作为提示。将一个 TextBlock(用于显示文本)拖到第一行(行偏移 0)、第一列,并将其大致放在中心。然后切换到属性窗口并设置以下属性:

  • Name=NamePrompt(在上方区域 6)
  • Common::Text=Full Name
  • Layout::Width=auto
  • Layout::Height=30
  • Layout::HorizontalAlignment=Right
  • Layout::VerticalAlignment=Center
  • Margin=5
  • Layout::Padding=0
  • Layout::MinWidth=30
  • Layout::MinHeight=30
  • Layout::MaxWidth=0
  • Layout::MaxHeight=0
  • Layout::Grid.Column=0
  • Layout::Grid.Columnspan=1
  • Layout::Grid.Row=0
  • Layout::Grid.Rowspn=1

添加其余提示

将六个额外的提示拖到表单上,设置方式相同,但 Text 和 Row 除外。行将是 1、2、3、4、5 和 7,文本将是:

  • Street Address
  • City, State, Zip
  • 电话
  • 传真
  • 电子邮件
  • 注释

您可以将其他所有内容保留原样,但“Text”下您将需要单击粗体按钮,并且您将需要设置提示名称(或使用默认名称,因为有有意义的名称的唯一原因是当您需要以编程方式访问对象时,而我们不会对这些标签这样做),并且不要忘记相应地设置 Grid.Row。

您跳过的行将用于 Male & Female RadioButtons,而没有提示。

如此之多的属性相同可能让您感到不安。别担心,我们可以使用样式来解决这个问题,这是后续教程中的一个主题。下面的 Xaml 是完整的,方便剪切和粘贴。

添加输入控件

接下来,您将需要添加输入控件。将 TextBox 对象添加到行 0、1、3、4、5 和 7。设置以下属性:

  • Name=Name (或 StreetAddress 等)
  • Common::Text=Full Name (或街道地址等)
  • Layout::Width=300
  • Layout::Height=70
  • Layout::HorizontalAlignment=Left
  • Layout::VerticalAlignment=Center
  • Margin=5
  • Layout::Padding=0
  • Layout::MinWidth=30
  • Layout::MinHeight=30
  • Layout::MaxWidth=0
  • Layout::MaxHeight=0
  • Layout::Grid.Column=0
  • Layout::Grid.Columnspan=1
  • Layout::Grid.Row=0
  • Layout::Grid.Rowspn=1

使用 Stack Panel

您跳过了第 2 行(从上往下第三行)的输入控件,因为您想在该行放置三个文本框(分别用于城市、州和邮政编码)。如果您将所有三个都设置为同一行和同一列,它们将堆叠在一起。您想要的是“堆叠”它们,但在这里不是一个堆叠在另一个之上,而是并排。

将 StackPanel 拖到表单上,并按如下方式设置其属性:

  • Common:Orientation = Horizontal
  • Layout::HorizontalAlignment=Stretch
  • Layout::VerticalAlignment=Stretch
  • Margin=0
  • Layout::Padding=0
  • Layout::MinWidth=0
  • Layout::MinHeight=0
  • Layout::MaxWidth=Infinity
  • Layout::MaxHeight=Infinity
  • Layout::Grid.Column=0
  • Layout::Grid.Columnspan=1
  • Layout::Grid.Row=0
  • Layout::Grid.Rowspn=1将水平和垂直对齐方式设置为拉伸且无边距会导致 StackPanel 扩展以填充单元格。接下来,在 StackPanel(顶部)内放置三个 TextBox 控件,并按如下方式设置其属性:
    • Name=City
    • Width=150
    • Height=70
    • HorizontalAlignment=Left
    • VerticalAlignment=Bottom
    • Margin=0,0,0,5
    • Text=City
    • Name=State
    • Width=74
    • Height=70
    • Margin=0,0,0,5
    • Text= State
    • Name=Zip
    • Width=93
    • Height=70
    • Margin=0,0,0,5
    • Text= Zip

请注意,StackPanel 中的控件不需要 Grid 坐标。

最后,您需要在行偏移量为 6 的位置添加两个 RadioButtons。再次创建一个 StackPanel,并在 StackPanel 中放置两个 RadioButton 控件:

  • Name=Male
  • Content=Male
  • GroupName=Sex
  • IsChecked=True
  • Name=Female
  • Content=Female
  • GroupName=Sex
  • IsChecked=False

GroupName 创建了一个单选按钮组,允许按钮互斥。

这就是您的 UI。

运行应用程序

在我们继续之前,如果您还没有这样做,请运行应用程序。您可以通过单击“生成”->“重新生成第一个应用程序”(或 Ctrl+Shift+B),然后单击“调试”->“运行”(F5)来执行此操作,或者您可以跳过第一步直接运行,因为这会强制重新生成。您的应用程序将在模拟器中启动,并填充了字段!太棒了!

当您欣赏完您应用程序的第一个版本后,请停止调试(按工具栏上的停止调试按钮或“调试”->“停止调试”),但不要关闭模拟器。模拟器需要一段时间才能“启动”,您可以让它一直运行;每次重新生成和重新运行时,它都会连接到您的程序。

数据绑定

不幸的是,我们很少被要求用已知值来填充表单。用数据填充表单的情况要常见得多,通常是从数据库检索数据,可能通过 Web 服务。本教程中我们不担心如何检索数据,但让我们深入了解数据绑定——这项技术可以告诉 View 如何获取它需要的数据。由于我们是使用 MVVM 进行此操作的,我们将让 ViewModel 从 Model 获取数据,根据业务规则对其进行消息处理,然后使用绑定允许 View 在 MainPage.xaml.cs 文件中不包含任何代码。(这极大地有助于测试)。

为了实现这一点,我们需要做两件事:

  • 在 Model 中创建数据
  • 创建 ViewModel,其中包含 View 可以绑定的公共属性
  • 更新 View…

好的,三件事,我们需要:

  • 在 Model 中创建数据
  • 创建 ViewModel,其中包含 View 可以绑定的公共属性
  • 更新 View,设置每个控件的绑定
  • 设置数据…

四件事!是的!四件事…

  • 在 Model 中创建数据
  • 创建 ViewModel,其中包含 View 可以绑定的公共属性
  • 更新 View,设置每个控件的绑定
  • 设置数据上下文
  • 漂亮的红色制服

在 Model 中创建数据

在“真实”应用程序中,您将从 Web 服务等数据源获取数据(该服务可能又从数据库获取数据),但现在我们将在本地代码中创建一个“Customer”类的实例。在 Model 文件夹中创建一个 Customer.cs 类,如下所示:

using System;
 
namespace Your_First_Application.Model
{
    public class Customer
    {
 
        public string First { get; set; }
        public string Last { get; set;  }
        public string Address { get; set; }
        public string City { get; set; }
        public string State { get; set; }
        public string Zip { get; set; }
        public string HomePhone { get; set; }
        public string WorkPhone { get; set; }
        public string Fax { get; set; }
        public string WorkEmail { get; set; }
        public string HomeEmail { get; set; }
        public bool IsMale { get; set; }
        public string Notes { get; set; }
        public int CreditRating { get; set;  }
        public DateTime FirstContact { get; set; }
        public DateTime LastContact { get; set; }
 
    }
}

C# 适用于 Objective-C 程序员

这些被称为“自动属性”。当您编写

public string First { get; set; }

编译器会将其完全视为您已经编写了

private string _secretvariable
public string First
{
   get { return _secretVariable; }
   set { _secretVariable = value; }
}

也就是说,就像您为该值(在本例中是字符串 _secretVariable)创建了一个后备存储,并且 get 访问器返回了该值。set 访问器将后备存储的值设置为传递给 set 访问器的值。以下是如何使用任一形式:

First = "Mary";        // call the setter
//....
string fn = First;     // call the getter

第一行调用 setter,字符串 Mary 作为value传入。

第二行表示中间代码。

第三行使用 getter 检索“Mary”并将其赋给本地字符串变量fn

为什么不使用字段?

如果我们使用自动属性,为什么不使用公共字段呢?Jon Galloway 指出这是一个常见问题,而 Scott Guthrie 给出了一个很好的答案:

请注意,我们实际上并没有在属性的 getter/setter 中添加任何逻辑——而是直接获取/设置值到字段。这就引出了一个问题——那么为什么不直接使用字段而不是属性呢?嗯——公开字段有很多缺点。两个主要问题是:1)您无法轻松地针对字段进行数据绑定,2)如果您从类中公开字段,则以后无法将其更改为属性(例如:在 setter 中添加验证逻辑)而无需重新编译已针对旧类编译的任何程序集。

完成 Customer 类

有了属性之后,我们需要一种生成实例的方法。最好是我们不需要实例就可以创建一个,所以我们将添加一个静态方法,该方法返回一个预填充的实例。静态方法可以在类而不是实例上调用(正如您一会儿将看到的)。

public static Customer GetCustomer()
{
    var newCust = new Customer(
        "Martha",
        "Washington",
        "1640 Pennsylvania Avenue",
        "New York",
        "NY",
        "11214",
        "617-555-4663",
        "781-555-9675",
        "212-555-5353",
        "jesseliberty@jesseliberty.com",
        "none",
        false,
        "VIP - Treat nicely",
        700,
        new DateTime(1955,07,10),
        new DateTime(2010,06,06));
    return newCust;
}

此方法仅创建类的实例,填充其字段,然后返回该实例。您永远不会在生产代码中这样做,但这确实为我们提供了一个 Customer 类的实例,就好像我们从 Web 服务获取了它一样。唯一缺少的是我们调用的构造函数(该构造函数为每个属性接收一个值)。

public Customer(
    string first,
    string last,
    string address,
    string city,
    string state,
    string zip,
    string homePhone,
    string workPhone,
    string fax,
    string workEmail,
    string homeEmail,
    bool isMale,
    string notes,
    Int16 creditRating,
        DateTime firstContact,
        DateTime lastContact )
{
    First = first;
    Last = last;
    Address = address;
    City = city;
    State = state;
    Zip = zip;
    HomePhone = homePhone;
    WorkPhone = workPhone;
    Fax = fax;
    HomeEmail = homeEmail;
    WorkEmail = workEmail;
    IsMale = isMale;
    Notes = notes;
    CreditRating = creditRating;
    FirstContact = firstContact;
    LastContact = lastContact;
 
}

这就是 Model。

ViewModel

ViewModel 的工作是管理此数据,将其绑定到 View,并处理用户输入和用户操作。我们将把用户输入和用户操作的讨论推迟到未来的教程,但让我们创建 ViewModel,其中包含我们在 View 中要显示的 Customer 中每个值的公共属性。这是完整的 MainViewModel.cs 文件:

using GalaSoft.MvvmLight;
using Your_First_Application.Model;
 
namespace Your_First_Application.ViewModel
{
    public class MainViewModel : ViewModelBase
    {
        public string ApplicationTitle
        {
            get { return "Your First Application"; }
        }
 
        public string PageName
        {
            get { return "Customer"; }
        }
 
        private readonly Customer cust = Customer.GetCustomer();
 
        public string Name { get { return cust.First + " " + cust.Last; } }
        public string Street { get { return cust.Address; } }
        public string City { get { return cust.City; } }
        public string State { get { return cust.State; } }
        public string Zip { get { return cust.Zip; } }
        public string Phone { get { return cust.WorkPhone; } }
        public string Fax { get { return cust.Fax; } }
        public string Email { get { return cust.WorkEmail; } }
        public bool IsMale { get { return cust.IsMale == true; } }
        public bool IsFemale { get { return !IsMale; } }
        public string Notes { get { return cust.Notes; } }
    }
}

前两个公共属性由 MVVM Light Toolkit 设置,底部的 11 个属性是我们将在 11 个用户控件上绑定的属性。

设置数据上下文

请注意,我们绑定的值是 customer 对象上的值。UI 元素如何知道哪个对象拥有这些属性?这由控件的数据上下文属性、其容器(grid)或容器的容器(页面)处理。使用 MVVM Light Toolkit,此工作由 DataContext Locator 对象完成,您会发现该对象已在 Xaml 文件中声明。

DataContext="{Binding Main, Source={StaticResource Locator}}">

此声明是由 MVVM Light Toolkit 为您创建的,因此您无需担心它;它就是这样工作的。

在 UI 级别进行绑定

Binding_thumb.jpg

现在是时候将控件绑定到 ViewModel 中的属性了。返回 View,然后单击其中一个 TextBox 控件。删除内容,然后选择“绑定”。打开的对话框将询问您希望绑定到哪个公共属性。

Text 属性行上的小金桶表示您有一个已知的数据源。打开的对话框有四个选项卡:

  • Path
  • 转换器
  • 选项

Source 是数据上下文(尽管您可能还有其他来源,这是一个稍后教程的主题)。

Path 是您要绑定的属性。

Converter 允许绑定对象转换类型和/或格式化数据。

Options 是您可以选择三种绑定类型之一的地方。

  1. One Time
  2. One Way
  3. Two Way

OneTime 绑定会设置绑定并关闭绑定连接。

OneWay 绑定适用于只读数据。

TwoWay 绑定允许用户更新您显示的数据。我们将在即将到来的教程中研究 TwoWay 绑定。

在 Xaml 中创建 UI

虽然拖放和设置属性效果很好,但您可能会发现您的 Silverlight 编程朋友不利用这一点。他们更愿意直接在 Xaml 中编写这些简单的 UI 设计。这很大程度上是以前只能直接编写 Xaml 的时代的遗留问题,但您可能对如何做到这一点感兴趣。简单,非常简单。返回分屏视图,检查生成的 Xaml。更改 Xaml 并查看 UI 如何变化。更改 UI 并查看 Xaml 如何变化。以下是我们页面的完整 Xaml:

<phone:PhoneApplicationPage x:Class="Your_First_Application.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"
                            FontFamily="{StaticResource PhoneFontFamilyNormal}"
                            FontSize="{StaticResource PhoneFontSizeNormal}"
                            Foreground="{StaticResource PhoneForegroundBrush}"
                            SupportedOrientations="Portrait"
                            Orientation="Portrait"
                            mc:Ignorable="d"
                            d:DesignWidth="480"
                            d:DesignHeight="768"
                            shell:SystemTray.IsVisible="True"
                            DataContext="{Binding Main, Source={StaticResource Locator}}">
 
   <!--LayoutRoot contains the root grid where all other page content is placed-->
   <Grid x:Name="LayoutRoot"
         Background="Transparent">
      <Grid.RowDefinitions>
         <RowDefinition Height="Auto" />
         <RowDefinition Height="*" />
      </Grid.RowDefinitions>
 
      <!--TitlePanel contains the name of the application and page title-->
      <StackPanel x:Name="TitlePanel"
                  Grid.Row="0"
                  Margin="24,24,0,12">
         <TextBlock x:Name="ApplicationTitle"
                    Text="{Binding ApplicationTitle}"
                    Style="{StaticResource PhoneTextNormalStyle}" />
         <TextBlock x:Name="PageTitle"
                    Text="{Binding PageName}"
                    Margin="-3,-8,0,0"
                    Style="{StaticResource PhoneTextTitle1Style}" />
      </StackPanel>
 
      <!--ContentPanel - place additional content here-->
      <Grid x:Name="ContentGrid"
            Grid.Row="1"
            Height="617"
            VerticalAlignment="Bottom">
         <Grid.RowDefinitions>
            <RowDefinition Height="1*" />
            <RowDefinition Height="1*" />
            <RowDefinition Height="1*" />
            <RowDefinition Height="1*" />
            <RowDefinition Height="1*" />
            <RowDefinition Height="1*" />
            <RowDefinition Height="1*" />
            <RowDefinition Height="1*" />
         </Grid.RowDefinitions>
         <Grid.ColumnDefinitions>
            <ColumnDefinition Width="1*" />
            <ColumnDefinition Width="2*" />
         </Grid.ColumnDefinitions>
         <TextBlock Height="30"
                    HorizontalAlignment="Right"
                    Margin="5"
                    Name="NamePrompt"
                    Text="Full Name"
                    VerticalAlignment="Center"
                    FontWeight="Bold"
                    Grid.Row="0" />
         <TextBlock FontWeight="Bold"
                    Height="30"
                    HorizontalAlignment="Right"
                    Name="streetPrompt"
                    Text="Street Address"
                    VerticalAlignment="Center"
                    Margin="5"
                    Grid.Row="1" />
         <TextBlock FontWeight="Bold"
                    Height="30"
                    HorizontalAlignment="Right"
                    Name="cityStateZipPrompt"
                    Text="City, State, Zip"
                    VerticalAlignment="Center"
                    Margin="5"
                    Grid.Row="2" />
         <TextBlock Height="30"
                    HorizontalAlignment="Right"
                    Margin="5"
                    Name="PhonePrompt"
                    Text="Phone"
                    VerticalAlignment="Center"
                    FontWeight="Bold"
                    Grid.Row="3" />
         <TextBlock FontWeight="Bold"
                    Height="30"
                    HorizontalAlignment="Right"
                    Name="FaxPrompt"
                    Text="Fax"
                    VerticalAlignment="Center"
                    Margin="5"
                    Grid.Row="4" />
         <TextBlock FontWeight="Bold"
                    Height="30"
                    HorizontalAlignment="Right"
                    Name="EmailPrompt"
                    Text="Email"
                    VerticalAlignment="Center"
                    Margin="5"
                    Grid.Row="5" />
 
         <TextBlock FontWeight="Bold"
                    Height="30"
                    HorizontalAlignment="Right"
                    Name="NotesPrompt"
                    Text="Notes"
                    VerticalAlignment="Center"
                    Margin="5"
                    Grid.Row="7" />
 
         <TextBox Name="FullName"
                  Width="303"
                  Height="70"
                  HorizontalAlignment="Left"
                  VerticalAlignment="Bottom"
                  Margin="5,0,0,5"
                  Grid.Column="1"
                  Text="{Binding Name}" />
         <TextBox Name="Street"
                  Width="303"
                  Height="70"
                  HorizontalAlignment="Left"
                  VerticalAlignment="Bottom"
                  Margin="5,0,0,5"
                  Grid.Row="1"
                  Grid.Column="1"
                  Text="{Binding Street}" />
         <StackPanel Orientation="Horizontal"
                     HorizontalAlignment="Stretch"
                     VerticalAlignment="Stretch"
                     Margin="0"
                     Grid.Row="2"
                     Grid.Column="1">
            <TextBox Name="City"
                     Width="150"
                     Height="70"
                     HorizontalAlignment="Left"
                     VerticalAlignment="Bottom"
                     Margin="0,0,0,5"
                     Text="{Binding City}" />
            <TextBox Name="State"
                     Width="74"
                     Height="70"
                     Margin="0,0,0,5"
                     Text="{Binding State}" />
            <TextBox Name="Zip"
                     Width="93"
                     Height="70"
                     Margin="0,0,0,5"
                     Text="{Binding Zip}" />
         </StackPanel>
         <TextBox Height="70"
                  HorizontalAlignment="Left"
                  Margin="5,0,0,5"
                  Name="Phone"
                  VerticalAlignment="Bottom"
                  Width="303"
                  Grid.Column="1"
                  Grid.Row="3"
                  Text="{Binding Phone}" />
         <TextBox Height="70"
                  HorizontalAlignment="Left"
                  Margin="5,0,0,5"
                  Name="Fax"
                  VerticalAlignment="Bottom"
                  Width="303"
                  Grid.Column="1"
                  Grid.Row="4"
                  Text="{Binding Fax}" />
         <TextBox Height="70"
                  HorizontalAlignment="Left"
                  Margin="5,0,0,5"
                  Name="Email"
                  VerticalAlignment="Bottom"
                  Width="303"
                  Grid.Column="1"
                  Grid.Row="5"
                  Text="{Binding Email}" />
         <StackPanel Orientation="Horizontal"
                     HorizontalAlignment="Stretch"
                     VerticalAlignment="Stretch"
                     Margin="5"
                     Grid.Row="6"
                     Grid.Column="1">
            <RadioButton Name="Male"
                         Content="Male"
                         GroupName="Sex"
                         IsChecked="{Binding IsMale}"
                         FontSize="20" />
            <RadioButton Name="Female"
                         Content="Female"
                         GroupName="Sex"
                         IsChecked="{Binding IsFemale}"
                         FontSize="20" />
         </StackPanel>
         <TextBox Height="72"
                  HorizontalAlignment="Left"
                  Margin="5,0,0,5"
                  Name="Notes"
                  VerticalAlignment="Bottom"
                  Width="290"
                  Grid.Column="1"
                  Grid.Row="7"
                  Text="{Binding Notes}" />
      </Grid>
   </Grid>
</phone:PhoneApplicationPage>

您可以在此处获取此应用程序的完整源代码。

避免属性包装器

肯定有人认为 ViewModel 中过多的属性只是传递 Customer 中相同属性的包装器。您可以通过使 Customer 对象成为 ViewModel 的公共属性,然后直接绑定到该对象来避免这种情况。如果您偏好该样式,则需要如下修改 ViewModel:

public class MainViewModel : ViewModelBase
{
    public string ApplicationTitle
    {
        get { return "Your First Application"; }
    }
 
    public string PageName
    {
        get { return "Customer"; }
    }
 
    private   Customer cust = Customer.GetCustomer();
    public   Customer Cust { get { return cust; } }
 
    public string Name { get { return cust.First + " " + cust.Last; } }
    public bool IsMale { get { return cust.IsMale == true; } }
    public bool IsFemale { get { return !IsMale; } }
}

您需要更新 Xaml 文件以绑定到 WorkPhone 和 WorkEmail(因为 ViewModel 不再为您进行转换)。

最后,您需要显式绑定到 Customer 属性的大部分字段,并将 DataContext 设置为 ViewModel(而不是 Customer)的几个属性(Name 和 Male/Female)的绑定。

public MainPage()
{
    InitializeComponent();
    var vm = new MainViewModel();
    DataContext = vm.Cust;
    FullName.DataContext = vm;
    Male.DataContext = Female.DataContext = vm;
}

我们创建 ViewModel 的一个实例,然后将 View 的 DataContext 设置为 ViewModel 的 Cust 属性。然后,我们将 FullName 的 DataContext 设置为 ViewModel 实例,最后我们将 Male.DataContext 和 Female.DataContext 也设置为 vm。

思维导图

这是喜欢这类东西的人喜欢的东西。

MindMapDataBinding2-300x212.jpg

© . All rights reserved.