精华内容
下载资源
问答
  • 数据WPF 使用数据绑定和 WPF 自定义数据显示 Josh Smith 本文将介绍以下内容: WPF 数据绑定数据显示和分层数据使用模板输入验证 本文使用以下技术:WPF、XAML、C# 代码下载位置:...
    数据和 WPF
    使用数据绑定和 WPF 自定义数据显示
    Josh Smith
    本文将介绍以下内容:
    • WPF 数据绑定
    • 数据显示和分层数据
    • 使用模板
    • 输入验证
    本文使用以下技术: 
    WPF、XAML、C#

    代码下载位置: AdvancedWPFDatabinding2008_07.exe (171 KB) 
    在线浏览代码
     Windows® Presentation Foundation (WPF) 首次出现在 .NET 雷达上时,大多数文章和演示应用程序都对其华丽的渲染引擎和 3D 性能大加宣扬。这些示例虽然读起来引人入胜、玩起来趣味横生,但却无法证明 WPF 在现实世界中的强大功能。那些在单击后会突然放出烟火的三维旋转视频固然很酷,但我们当中的大多数人都不会用它创建应用程序。创建软件来显示和编辑大量复杂的业务或科学数据才是我们的衣食父母。
    让人振奋的是,WPF 为管理显示和编辑复杂数据提供了良好的支持。在 2007 年 12 月刊的《MSDN® 杂志上,John Papa 撰写了“WPF 中的数据绑定”一文 (msdn.microsoft.com/magazine/cc163299),其中对 WPF 数据绑定的重要概念做了出色的介绍。在此,我将以 John 在上述数据点专栏中讲到的内容为基础,探讨一些更高级的数据绑定方案。研究过这些方案后,您将了解到在大多数行业应用程序中达到常用数据绑定要求的各种方法。

    在代码中绑定
    WPF 为桌面应用程序开发人员带来的最大变化之一就是广泛地使用和支持声明性编程。WPF 用户界面和资源可通过使用基于 XML 的标准标记语言 - 可扩展应用程序标记语言 (XAML) 来声明。大多数 WPF 数据绑定的说明只展示了如何在 XAML 中使用绑定。由于在 XAML 中执行的所有操作都可在代码中实现,因此专业 WPF 开发人员学习如何以编程方式及声明方式来使用数据绑定就显得尤为重要。
    许多情况下,在 XAML 中声明绑定更为便利。随着系统变得更加复杂和更加动态,有时在代码中使用绑定更为适宜。在继续深入之前,让我们首先回顾一下编程式数据绑定中涉及到的一些常用的类和方法。
    WPF 元素从 FrameworkElement 或实体框架 ContentElement 那里同时继承了 SetBinding 和 GetBinding 表达式方法。它们恰好是在 BindingOperations 实用程序类中可调用相同名称方法的便捷方法。下列代码说明了如何使用 Binding­Operations 类将某个文本框的 Text 属性绑定到另一个对象的属性上:
    static void BindText(TextBox textBox, string property)
    {
          DependencyProperty textProp = TextBox.TextProperty;
          if (!BindingOperations.IsDataBound(textBox, textProp))
          {
              Binding b = new Binding(property);
              BindingOperations.SetBinding(textBox, textProp, b);
          }
    } 
    
    您可以使用此处显示的代码轻松地取消绑定某个属性:
    static void UnbindText(TextBox textBox)
    {
        DependencyProperty textProp = TextBox.TextProperty;
        if (BindingOperations.IsDataBound(textBox, textProp))
        {
            BindingOperations.ClearBinding(textBox, textProp);
        }
    }
    
    通过清除绑定,您还可以移除目标属性的绑定值。
    在 XAML 中声明数据绑定会隐藏某些基本细节。一旦您开始在代码中使用绑定,这些细节就会显现出来。其中之一就是绑定源与目标之间的关系实际上是由 BindingExpression 类(而不是 Binding 自身)维持的。Binding 类中包含多个 BindingExpressions 可共享的高级信息,但两个绑定属性之间的联系是由基础表达式决定的。下列代码说明了您如何使用 BindingExpression 以编程方式检查某个文本框的 Text 属性是否经过了验证:
    static bool IsTextValidated(TextBox textBox)
    {
        DependencyProperty textProp = TextBox.TextProperty;
    
        var expr = textBox.GetBindingExpression(textProp);
        if (expr == null)
            return false;
    
        Binding b = expr.ParentBinding;
        return b.ValidationRules.Any();
    } 
    
    由于 BindingExpression 不知道其是否已验证,您需要询问其父项绑定。稍后我将分析一下输入验证技术。
    虚拟实验室:高级 WPF 数据绑定
    WPF 为管理显示和编辑复杂数据提供了良好的支持。只要使用几行 XAML,您就可以显示分层数据结构或验证用户输入。您可以在我们预置的虚拟实验室中找到 WPF 数据绑定。所有内容都已安装完成,准备就绪,包括本文中介绍的项目。只需根据显示的内容开始实验和编码即可。

    在虚拟实验室中进行试验:

    使用模板
    如用户界面富有成效,它提供的原始数据可以使用户直观地从中发现有意义的信息。这就是数据可视化的本质。数据绑定就像一道数据可视化难题。几乎所有最繁琐的 WPF 程序都需要一种更有效的数据提供方式,而不是简单地将某个控件属性绑定到数据对象的某个属性上。真实的数据对象具有多个相关值,这些不同的值应合并为一个组织严密的直观表示。这就是 WPF 使用数据模板的原因。
    System.Windows.DataTemplate 类就是 WPF 中的一种模板。通常,模板就像 WPF 框架使用的一种“俗套”,它创建的可视元素协助呈现那些没有固有直观表示的对象。如某个元素尝试显示一个没有固有直观表示的对象(如自定义业务对象),您可以通过赋予 DataTemplate 来告知元素如何呈现该对象。
    DataTemplate 会根据需要尽可能多地产生可视元素来显示该数据对象。这些元素使用数据绑定显示该数据对象的属性值。如果某个元素不知道如何显示通知其呈现的对象,就会对其调用 ToString 方法并在 TextBlock 中显示结果。
    假定您有一个名为 FullName 的简单类,用于存储人名。您想显示一个姓名列表,并使每个人的姓氏突出显示。要执行此操作,您需要创建一个描述如何呈现 FullName 对象的 DataTemplate。图 1 中所列代码显示了 FullName 类以及将显示姓名列表的窗口的源代码。
    public class FullName
    {
        public string FirstName { get; set; }
        public char MiddleInitial { get; set; }
        public string LastName { get; set; }
    }
    
    public partial class WorkingWithTemplates : Window
    {
        // This is the Window's constructor.
        public WorkingWithTemplates()
        {
            InitializeComponent();
    
            base.DataContext = new FullName[]
            {
                new FullName 
                { 
                    FirstName = "Johann", 
                    MiddleInitial = 'S', 
                    LastName = "Bach" 
                },
                new FullName 
                { 
                    FirstName = "Gustav",
                    MiddleInitial = ' ',
                    LastName = "Mahler" 
                },
                new FullName 
                { 
                    FirstName = "Alfred", 
                    MiddleInitial = 'G', 
                    LastName = "Schnittke" 
                }
            };
        }
    }
    
    图 2 所示,窗口的 XAML 文件中含有一个 ItemsControl。它创建了一个用户无法选择或移除的项目列表。ItemsControl 向其 ItemTemplate 属性分配一个 DataTemplate,利用它来呈现在窗口构造函数中创建的各个 FullName 实例。您应该注意 DataTemplate 中的大部分 TextBlock 元素是如何将其 Text 属性绑定到了它们所代表的 FullName 对象的属性上的。
    <!-- This displays the FullName objects. -->
    <ItemsControl ItemsSource="{Binding Path=.}">
      <ItemsControl.ItemTemplate>
        <DataTemplate>
          <StackPanel Orientation="Horizontal">
            <TextBlock FontWeight="Bold" Text="{Binding LastName}" />
            <TextBlock Text=", " />
            <TextBlock Text="{Binding FirstName}" />
            <TextBlock Text=" " />
            <TextBlock Text="{Binding MiddleInitial}" />
          </StackPanel>
        </DataTemplate>
      </ItemsControl.ItemTemplate>
    </ItemsControl>
    
    当运行此演示应用程序时,其外观类似于图 3 所示。通过使用 DataTemplate 来呈现名称可以很容易地突出显示每个人的姓氏,因为相应的 TextBlock 的 FontWeight 均为粗体。这个简单的示例说明了 WPF 数据绑定与模板之间的基本关系。进一步探讨该主题时,我将会把这些功能组合成更强大的复杂对象可视化方法。
    图 3 由 DataTemplate 呈现的 FullNames

    使用继承的 DataContext
    默认情况下,所有绑定均隐式绑定在某个元素的 DataContext 属性上。因此,可以说是元素的 DataContext 引用了其数据源。在 DataContext 的工作方式上,需要了解一些特殊的内容。在您了解了 DataContext 这个微妙的方面后,将极大地简化复杂数据绑定用户界面的设计。
    并非必须设置元素的 DataContext 属性才能引用数据源对象。如果元素树(从技术上讲是逻辑树)中某个祖先元素的 DataContext 被赋值,则该值将自动被用户界面中的每个后代元素所继承。换言之,如果窗口的 DataContext 设置为引用 Foo 对象,则默认情况下,窗口中每个元素的 DataContext 都将引用同一 Foo 对象。您可以轻松地为窗口中的任何元素分配一个不同的 DataContext 值,这会使该元素的所有后代元素都将继承新的 DataContext 值。这与 Windows 窗体中的环境属性很相似。
    在上一部分中,我讨论了如何使用 DataTemplates 创建可视化数据对象。图 2 中由模板创建的元素将其属性绑定到某个 FullName 对象的属性。这些元素隐式地绑定到其 DataContext 上。由 DataTemplate 创建的元素的 DataContext 引用模板所使用的数据对象,如 FullName 对象。
    DataContext 属性的值继承中没有任何神奇之处。它只是利用了对 WPF 中内置继承依赖关系属性的支持。任何依赖关系属性都可以是继承属性,在向 WPF 的依赖关系属性系统注册该属性时,只需在提供的元数据中指定标记即可。
    继承依赖关系属性的另一个示例是 FontSize,它含有所有元素。如果您在窗口中设置了 FontSize 依赖关系属性,则默认情况下,该窗口中的所有元素都将以该大小显示其文本。沿元素树向下传播 FontSize 值所使用的基础结构与传播 DataContext 的基础结构相同。
    在面向对象环境中的“继承”表示子类继承其父类的成员,而这里使用的术语“继承”有所不同。属性值继承仅指值在运行时沿着元素树向下的传播。在面向对象含义中,类当然可以继承那些支持值继承的依赖关系属性。

    使用集合视图
    当 WPF 控件绑定到数据集合上时,它们不会直接绑定在集合本身上。而是隐式地绑定在自动封装该集合的视图上。该视图可实现 ICollectionView 界面,可以是若干具体实现之一,如 ListCollectionView。
    一个集合视图有多项职责。它可跟踪集合中的当前项,该项通常会转换为列表控件中的活动/选定项。集合视图还提供在列表内排序、筛选和归组项目的一般方法。可以围绕集合将多个控件绑定到同一视图上,以便它们形成彼此并列的关系。以下代码显示了 ICollectionView 的一些功能:
    // Get the default view wrapped around the list of Customers.
    ICollectionView view = CollectionViewSource.GetDefaultView(allCustomers);
    
    // Get the Customer selected in the UI.
    Customer selectedCustomer = view.CurrentItem as Customer;
    
    // Set the selected Customer in the UI.
    view.MoveCurrentTo(someOtherCustomer);
    
    所有列表控件(如列表框、组合框和列表视图)必须将它们的 IsSynchronizedWithCurrentItem 属性设置为 true,以与集合视图的 Current­Item 属性保持同步。抽象的 Selector 类定义了该属性。如果未设置为 true,选择列表控件中的某项将不会更新该集合视图的 CurrentItem,向 CurrentItem 分配新值也不会在列表控件中有所反映。

    使用分层数据
    现实生活中到处都是分层数据。客户下达多重订单、分子由很多个原子组成、部门由多名员工组成,太阳系包含一系列的天体。您肯定对这种常见的主从复合排列非常熟悉。
    WPF 提供了多种使用分层数据结构的方法,分别适用于不同的情形。从本质上看,这是在选择使用多个控件显示数据,还是在一个控件中显示多层数据。接下来,我就要讲述这两种方法。

    使用多个控件显示 XML 数据
    一种极为常见的分层数据处理方法就是利用单独的控件显示各个层级。例如,假设我们有一个表示客户、订单和订单详细信息的系统。在此情况下,我们可能需要一个组合框来显示客户,用列表框显示所有选定的客户订单,然后用 ItemsControl 显示所选订单的相关详细信息。这是一种显示分层数据的不错的方法,极易在 WPF 中实现。
    根据我之前描述的场景,图 4 显示了某个数据的简化示例,它封装在 WPF XmlDataProvider 组件中,可供应用程序处理。一个类似于图 5 的用户界面将显示该数据。注意客户和订单是可选的,而订单的详细信息是存在于只读列表中的。这一点非常有意义,因为可视对象应仅在影响应用程序的状态时或处于可编辑状态时才可供选择。
    <XmlDataProvider x:Key="xmlData">
      <x:XData>
        <customers >
          <customer name="Customer 1">
            <order desc="Big Order">
              <orderDetail product="Glue" quantity="21" />
              <orderDetail product="Fudge" quantity="32" />
              </order>
              <order desc="Little Order">
                <orderDetail product="Ham" quantity="1" />
                <orderDetail product="Yarn" quantity="2" />
              </order>
            </customer>
            <customer name="Customer 2">
              <order desc="First Order">
                <orderDetail product="Mousetrap" quantity="4" />
              </order>
            </customer>
          </customers>
        </x:XData>
    </XmlDataProvider>
    
    图 5 显示 XML 数据的一种方法
    图 6 中的 XAML 介绍了如何使用这些不同的控件来显示刚才所示的分层数据。此窗口不需要任何代码,它完全存在于 XAML 中。
    <Grid DataContext=
        "{Binding Source={StaticResource xmlData},
        XPath=customers/customer}"
        Margin="4" 
      >
      <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <RowDefinition />
        <RowDefinition />
      </Grid.RowDefinitions>
    
      <!-- CUSTOMERS -->
      <DockPanel Grid.Row="0">
        <TextBlock DockPanel.Dock="Top" FontWeight="Bold" Text="Customers" />
        <ComboBox
          IsSynchronizedWithCurrentItem="True"
          ItemsSource="{Binding}"
          >
          <ComboBox.ItemTemplate>
            <DataTemplate>
              <TextBlock Text="{Binding XPath=@name}" />
            </DataTemplate>
          </ComboBox.ItemTemplate>
        </ComboBox>
      </DockPanel>
    
      <!-- ORDERS -->
      <DockPanel Grid.Row="1">
        <TextBlock DockPanel.Dock="Top" FontWeight="Bold" Text="Orders" />
        <ListBox
          x:Name="orderSelector" 
          DataContext="{Binding Path=CurrentItem}"
          IsSynchronizedWithCurrentItem="True" 
          ItemsSource="{Binding XPath=order}"
          >
          <ListBox.ItemTemplate>
            <DataTemplate>
              <TextBlock Text="{Binding XPath=@desc}" />
            </DataTemplate>
          </ListBox.ItemTemplate>
        </ListBox>
      </DockPanel>
    
      <!-- ORDER DETAILS -->
      <DockPanel Grid.Row="2">
        <TextBlock DockPanel.Dock="Top" FontWeight="Bold" 
          Text="Order Details" />
        <ItemsControl 
          DataContext=
             "{Binding ElementName=orderSelector, Path=SelectedItem}"
          ItemsSource="{Binding XPath=orderDetail}">
          <ItemsControl.ItemTemplate>
            <DataTemplate>
              <TextBlock>
                <Run>Product:</Run>
                <TextBlock Text="{Binding XPath=@product}" />
                <Run>(</Run>
                <TextBlock Text="{Binding XPath=@quantity}" />
                <Run>)</Run>
              </TextBlock>
            </DataTemplate>
          </ItemsControl.ItemTemplate>
        </ItemsControl>
      </DockPanel>
    </Grid>
    
    注意,在这里广泛使用了短 XPath 查询通知 WPF 在哪里获取绑定值。绑定类向您提供 XPath 属性,您可以为其分配 XmlNode.SelectNodes 方法支持的任何 XPath 查询。实质上,WPF 是使用该方法来执行 XPath 查询的。遗憾的是,这意味着由于 XmlNode.SelectNodes 当前不支持使用 XPath 函数,WPF 数据绑定也不支持它们。
    客户组合框以及订单列表框都绑定到 Xpath 查询(由根网格的 DataContext 绑定执行)的合成节点集上。列表框的 DataContext 将自动返回集合视图的 CurrentItem,该集合视图封装为网格的 DataContext 生成的 XmlNodes 集合。换言之,列表框的 DataContext 是当前选定的 Customer。由于该列表框的 ItemsSource 隐式地绑定到其自己的 DataContext(因为没有指定任何其他源)上,且其 ItemsSource 绑定会执行 XPath 查询以从 DataContext 中获取 <order> 元素,因此 ItemsSource 将有效地绑定到选定客户的订单列表上。
    请记住,在绑定到 XML 数据时,您实际上是绑定到由对 XmlNode.SelectNodes 的调用创建的对象上。如果不仔细,您就会最终将多个控件绑定到逻辑上等效但实际不同的多组 XmlNodes 上。这是由于每次调用 XmlNode.SelectNodes 都会生成一组新的 XmlNodes,即使您每次都是将相同的 XPath 查询传递给同一 XmlNode 也是如此。这是需要对 XML 数据绑定特别注意的一点,在绑定业务对象时,可将其忽略。

    使用多个控件显示业务对象
    假设您现在要绑定到上一示例的同一数据中,但数据以业务对象而不是 XML 的形式存在。这会对您绑定数据结构各个层次的方法有何影响?使用的技术会有何相同或不同之处?
    图 7 中的代码显示了一个简单类,它用于创建业务对象,其中存储着我们将要绑定的数据。这些类构成的逻辑架构与前一部分中 XML 数据所使用的架构相同。
    public class Customer
    {
        public string Name { get; set; }
        public List<Order> Orders { get; set; }
    
        public override string ToString()
        {
            return this.Name;
        }
    }
    
    public class Order
    {
        public string Desc { get; set; }
        public List<OrderDetail> OrderDetails { get; set; }
    
        public override string ToString()
        {
            return this.Desc;
        }
    }
    
    public class OrderDetail
    {
        public string Product { get; set; }
        public int Quantity { get; set; }
    }
    
    图 8 所示为这些对象显示窗口的 XAML。它与图 6 中看到的 XAML 很相似,但其中一些重要的差异需要注意。XAML 中没有显示出是该窗口的构造函数创建了数据对象并设置了 DataContext,而不是 XAML 将其作为资源进行引用。请注意,其中没有任何控件显式设置了 DataContext。它们全部都继承了同一个 DataContext,该 DataContext 是一个 List<Customer> 实例。
    <Grid Margin="4">
      <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <RowDefinition />
        <RowDefinition />
      </Grid.RowDefinitions>
    
       <!-- CUSTOMERS -->
       <DockPanel Grid.Row="0">
         <TextBlock DockPanel.Dock="Top" FontWeight="Bold" Text="Customers"
         />
         <ComboBox 
           IsSynchronizedWithCurrentItem="True" 
           ItemsSource="{Binding Path=.}" 
           />
       </DockPanel>
    
       <!-- ORDERS -->
       <DockPanel Grid.Row="1">
         <TextBlock DockPanel.Dock="Top" FontWeight="Bold" Text="Orders" />
         <ListBox 
           IsSynchronizedWithCurrentItem="True" 
           ItemsSource="{Binding Path=CurrentItem.Orders}" 
           />
       </DockPanel>
    
       <!-- ORDER DETAILS -->
       <DockPanel Grid.Row="2">
         <TextBlock DockPanel.Dock="Top" FontWeight="Bold" 
            Text="Order Details" />
         <ItemsControl
           ItemsSource="{Binding Path=CurrentItem.Orders.CurrentItem.
           OrderDetails}"
           >
           <ItemsControl.ItemTemplate>
             <DataTemplate>
               <TextBlock>
                 <Run>Product:</Run>
                 <TextBlock Text="{Binding Path=Product}" />
                 <Run>(</Run>
                 <TextBlock Text="{Binding Path=Quantity}" />
                 <Run>)</Run>
               </TextBlock>
             </DataTemplate>
           </ItemsControl.ItemTemplate>
         </ItemsControl>
       </DockPanel>
    </Grid>
    
    绑定到业务对象(而非 XML)时,另一个明显的区别是,承载订单详细信息的 ItemsControl 不需要绑定到订单列表框的 SelectedItem。该方法在 XML 绑定中是必要的,因为对于列表项来自本地 XPath 查询的列表,没有任何通用的方式可以用来引用它的当前项。
    在绑定到业务对象(而不是 XML)时,通常是按照所选项的嵌套级别来绑定的。ItemsControl 的 ItemsSource 绑定利用了这个便捷的特点,在绑定路径中两次指定 CurrentItem: 一次是针对所选客户,一次是针对所选订单。如前文所述,CurrentItem 属性是封装数据源的基础 ICollectionView 的成员。
    就 XML 和业务对象之间在工作方式上的差异而言,还有一点需要注意。由于 XML 示例绑定到 XmlElements,因此必须提供 DataTemplates 以解释如何呈现客户和订单。在绑定到自定义业务对象时,只需覆盖 Customer 类和 Order 类的 ToString 方法,并允许 WPF 为这些对象显示该方法的输出即可避免这种开销。这一窍门仅够处理具有简单文本表示的对象。在处理复杂的数据对象时,使用这种便捷的技术可能不适用。

    一个用于显示整个层次结构的控件
    到现在为止,您只了解了通过在不同的控件中显示层次结构的每个层级来展示分层数据的方法。但通常需要在同一控件中显示分层数据的所有层级,这对数据处理是有帮助的,也是必要的。此方法的典型例子就是 TreeView 控件,该控件支持显示和浏览任意层级的嵌套数据。
    您可通过以下两种方式用各项来填充 WPF TreeView。一种方法以代码或 XAML 方式手动添加项,另一种方法是通过数据绑定创建这些项。
    下面的 XAML 表明了如何通过 XAML 将一些 TreeViewItem 手动添加到 TreeView:
    <TreeView>
      <TreeViewItem Header="Item 1">
        <TreeViewItem Header="Sub-Item 1" />
        <TreeViewItem Header="Sub-Item 2" />
      </TreeViewItem>
      <TreeViewItem Header="Item 2" />
    </TreeView>
    
    如控件始终都显示小型静态项组,在 TreeView 中手动创建项的方法是很实用的。如果需要显示大量会随时间变化的数据,就必须使用更具动态性的方法。此时,您有两种方法可供选择。您可以编写这样的代码:从头至尾检查整个数据结构,根据找到的数据对象创建 TreeViewItem,然后将这些项添加到 TreeView。或者利用分层数据模板,让 WPF 代您完成所有工作。

    使用分层数据模板
    您可以用声明的方式解释 WPF 应如何通过分层数据模板呈现分层数据。利用 HierarchicalDataTemplate 类这一工具可以弥补复杂数据结构与该数据的直观表示之间的缺口。它与常用 DataTemplate 非常相似,但还允许您指定数据对象子项的来源。您还可以为 HierarchicalDataTemplate 提供一个用于呈现这些子项的模板。
    假定您现在要在一个 TreeView 控件中显示图 7 中展现的数据。该 TreeView 控件看上去可能有些类似于图 9。实现此控件需要使用两个 HierarchicalDataTemplate 和一个 DataTemplate。
    图 9 在 TreeView 中显示整个数据层次结构
    这两个分层模板可显示 Customer 对象和 Order 对象。由于 OrderDetail 对象没有任何子项,您可以通过非分层 DataTemplate 呈现这些对象。TreeView 的 ItemTemplate 属性会将该模板用于 Customer 类型的对象,因为 Customer 是包含在 TreeView 根层级下的数据对象。图 10 中所列的 XAML 表明了如何将所有这些方式有机地结合在一起。
    <Grid>
      <Grid.DataContext>
        <!-- 
        This sets the DataContext of the UI
        to a Customers returned by calling
        the static CreateCustomers method. 
        -->
        <ObjectDataProvider 
          xmlns:local="clr-namespace:VariousBindingExamples"
          ObjectType="{x:Type local:Customer}"
          MethodName="CreateCustomers"
          />
      </Grid.DataContext>
    
      <Grid.Resources>
        <!-- ORDER DETAIL TEMPLATE -->
        <DataTemplate x:Key="OrderDetailTemplate">
          <TextBlock>
            <Run>Product:</Run>
            <TextBlock Text="{Binding Path=Product}" />
            <Run>(</Run>
            <TextBlock Text="{Binding Path=Quantity}" />
            <Run>)</Run>
          </TextBlock>
        </DataTemplate>
    
        <!-- ORDER TEMPLATE -->
        <HierarchicalDataTemplate 
          x:Key="OrderTemplate"
          ItemsSource="{Binding Path=OrderDetails}"
          ItemTemplate="{StaticResource OrderDetailTemplate}"
          >
          <TextBlock Text="{Binding Path=Desc}" />
        </HierarchicalDataTemplate>
    
        <!-- CUSTOMER TEMPLATE -->
        <HierarchicalDataTemplate 
          x:Key="CustomerTemplate"
          ItemsSource="{Binding Path=Orders}"
          ItemTemplate="{StaticResource OrderTemplate}"
          >
          <TextBlock Text="{Binding Path=Name}" />
        </HierarchicalDataTemplate>
      </Grid.Resources>
    
      <TreeView
        ItemsSource="{Binding Path=.}"
        ItemTemplate="{StaticResource CustomerTemplate}"
        />
    
    </Grid>
    
    我为 Grid(包含 TreeView )的 DataContext 分配了一个 Customer 对象集合。这在 XAML 中通过使用 ObjectDataProvider 即可实现,它是从 XAML 调用方法的一种便捷方式。由于 DataContext 是在元素树中自上而下地继承,因此 TreeView 的 DataContext 会引用这组 Customer 对象。这就是我们可以为其 ItemsSource 属性提供一个 "{Binding Path=.}" 绑定的原因,通过这种方式可以表明 ItemsSource 属性被绑定到 TreeView 的 DataContext。
    如果没有分配 TreeView 的 ItemTemplate 属性,则 TreeView 将仅显示顶层的 Customer 对象。由于 WPF 不知道如何呈现 Customer,因此它会对每个 Customer 都调用 ToString,并为每项都显示该文本。它无法确定每个 Customer 都有一个与其关联的 Order 对象列表,且每个 Order 都有一个 OrderDetail 对象列表。由于 WPF 无法理解您的数据架构,因此您必须向 WPF 解释架构,使它能正确呈现数据结构。
    向 WPF 解释数据的结构和外观是 HierarchicalDataTemplates 的份内工作。此演示中所使用的模板包含的可视元素树非常简单,就是其中带有少量文本的 TextBlocks。在更复杂的应用程序中,模板可能包含交互式的旋转 3D 模型、图像、矢量绘图、复杂的 UserControl 或任何其他可视化基础数据对象的 WPF 内容。
    需要特别注意声明模板的顺序。必须先声明一个模板后才能通过 StaticResource 扩展对其进行引用。这是由 XAML 阅读器规定的要求,该要求适用于所有资源,而不仅仅是模板。
    或者可通过使用 DynamicResource 扩展来引用模板,在这种情况下,模板声明的词汇顺序无关紧要。但使用 DynamicResource 引用(而不是 StaticResource 引用)总会带来一些运行时开销,因为它们要监控资源系统的更改。由于我们不会在运行时替换模板,因此这笔开销是多余的,最好使用 StaticResource 引用并恰当地安排模板声明的顺序。

    使用用户输入
    对大多数程序而言,显示数据仅仅是这场较量的一半。另一个巨大的挑战是分析、接受和拒绝用户输入的数据。理想状态下,所有用户都始终输入符合逻辑且准确的数据,那么这会是一项简单的任务。但在现实生活中,却根本不是这么回事。现实中的用户会出现打字错误、忘记输入所需的值、在错误的位置输入值、删除不应删除的记录、添加本不该有的记录,人都会犯错误。
    作为开发人员和架构师,我们的工作就是与无法避免的错误和恶意用户输入作斗争。WPF 绑定基础结构支持输入验证。在本文接下来的几节中,我将讲述如何充分利用 WPF 对验证的支持,以及如何向用户显示验证错误消息。

    通过 ValidationRules 验证输入
    WPF 的第一个版本包含在 Microsoft® .NET Framework 3.0 之内,只能有限地支持输入验证。Binding 类具有 ValidationRules 属性,它可存储任意数量的 ValidationRule 派生类。这些规则中的每一个都包含一些可通过测试查看绑定值是否有效的逻辑。
    那时,WPF 仅随一个被称为 ExceptionValidationRule 的 ValidationRule 子类出现。开发人员可将该规则添加到绑定的 ValidationRules 中,它将捕捉在数据源更新过程中抛出的异常,从而允许 UI 显示异常的错误消息。这种输入验证方法的有效性极富争议,人们认为良好用户体验的基础是避免不必要地向用户泄露技术细节。对于大多数用户而言,数据分析异常的错误消息通常过于专业,对不起,这个话题有点离题。
    假设您有一个表示时间段的类,例如这里看到的简单 Era 类:
    public class Era
    {
        public DateTime StartDate { get; set; }
        public TimeSpan Duration { get; set; }
    }
    
    如果您希望允许用户编辑某个时间段的开始日期和持续时间,您可以使用两个文本框控件,然后将它们的 Text 属性绑定到 Era 实例的属性上。由于用户可在文本框内输入任何他想输入的文本,您无法确保输入的文本可转换为 DateTime 或 TimeSpan 的实例。在这种情况下,您可以使用 ExceptionValidationRule 报告数据转换错误,然后在用户界面上显示这些转换错误。图 11 中所列的 XAML 展示了完成此任务的方式。
    <!-- START DATE -->
    <TextBlock Grid.Row="0">Start Date:</TextBlock>
    <TextBox Grid.Row="1">
      <TextBox.Text>
        <Binding Path="StartDate" UpdateSourceTrigger="PropertyChanged">
          <Binding.ValidationRules>
            <ExceptionValidationRule />
          </Binding.ValidationRules>
        </Binding>
      </TextBox.Text>
    </TextBox>
    
    <!-- DURATION -->
    <TextBlock Grid.Row="2">Duration:</TextBlock>
    <TextBox 
      Grid.Row="3" 
      Text="{Binding 
             Path=Duration, 
             UpdateSourceTrigger=PropertyChanged, 
             ValidatesOnExceptions=True}" 
      />
    
    这两个文本框展示了在 XAML 中将 ExceptionValidationRule 添加到绑定的 ValidationRules 中的两种方法。“开始日期”文本框使用详细的属性元素语法显式地添加规则。“持续时间”文本框使用简写语法,将绑定的 ValidatesOnExceptions 属性设置为 true。这两类绑定都将其 UpdateSourceTrigger 属性设置为 PropertyChanged,这样每次为文本框的 Text 属性赋新值时都会进行验证,而不是空等控件。该程序的屏幕快照如图 12 所示。
    图 12 ExceptionValidationRule 显示验证错误

    显示验证错误
    图 13 所示,“Duration”(持续时间)文本框中包含一个无效值。其包含的字符串无法转换为 TimeSpan 实例。该文本框的工具提示显示了一则错误消息,控件的右侧出现一个小的红色错误图标。这种行为并不会自动发生,但很容易实现和自定义。
    <!-- 
    The template which renders a TextBox 
    when it contains invalid data. 
    -->
    <ControlTemplate x:Key="TextBoxErrorTemplate">
      <DockPanel>
        <Ellipse 
          DockPanel.Dock="Right" 
          Margin="2,0"
          ToolTip="Contains invalid data"
          Width="10" Height="10"   
          >
          <Ellipse.Fill>
            <LinearGradientBrush>
              <GradientStop Color="#11FF1111" Offset="0" />
              <GradientStop Color="#FFFF0000" Offset="1" />
            </LinearGradientBrush>
          </Ellipse.Fill>
        </Ellipse>
        <!-- 
        This placeholder occupies where the TextBox will appear. 
        -->
        <AdornedElementPlaceholder />
      </DockPanel>
    </ControlTemplate>
    
    <!-- 
    The Style applied to both TextBox controls in the UI.
    -->
    <Style TargetType="TextBox">
      <Setter Property="Margin" Value="4,4,10,4" />
      <Setter 
        Property="Validation.ErrorTemplate" 
        Value="{StaticResource TextBoxErrorTemplate}" 
        />
      <Style.Triggers>
        <Trigger Property="Validation.HasError" Value="True">
          <Setter Property="ToolTip">
            <Setter.Value>
              <Binding 
                Path="(Validation.Errors)[0].ErrorContent"
                RelativeSource="{x:Static RelativeSource.Self}"
                />
            </Setter.Value>
          </Setter>
        </Trigger>
      </Style.Triggers>
    </Style>
    
    静态 Validation 类通过使用某些附加的属性和静态方法在控件与其包含的任何验证错误之间形成关系。您可以在 XAML 中引用这些附加属性,就用户界面向用户提供输入验证错误的方法创建说明,将其做为标记。图 13 中的 XAML 负责解释如何呈现上一个示例中两个文本框控件的输入错误消息。
    图 13 中 Style 的目标是 UI 中文本框的所有实例。它对文本框应用了三种设置。第一个 Setter 影响文本框的 Margin 属性。Margin 属性可设置为适当的值,以提供足够的空间在右侧显示错误图标。
    Style 中的下一个 Setter 分配 ControlTemplate,在包含无效数据时它负责呈现文本框。它将附加的 Validation.ErrorTemplate 属性设置为在 Style 之上声明的 ControlTemplate。当 Validation 类报告文本框有一处或多处验证错误时,该文本框将随该模板一同呈现。这里就是红色错误图标的来源,如图 12 所示。
    Style 还包含一个用于监控文本框上附加的 Validation.HasError 属性的 Trigger。当 Validation 类为文本框将附加的 HasError 属性设置为 true 时,Style 的 Trigger 激活并向文本框分配一条工具提示。工具提示的内容绑定为异常的错误消息,该异常在尝试将文本框的文本解析为源属性的数据类型实例时抛出。

    通过 IDataErrorInfo 验证输入
    在引入 Microsoft .NET Framework 3.5 后,WPF 对输入验证的支持得到了显著改进。ValidationRule 方法对于简单的应用程序很有用,但现实中的应用程序需要处理复杂的真实数据和业务规则。将业务规则编码到 ValidationRule 对象中不仅仅是将代码捆绑到 WPF 平台上,还不允许业务逻辑在其所属位置:业务对象中存在!
    很多应用程序都有一个业务层,该层的一组业务对象中包含复杂的业务处理规则。针对 Microsoft .NET Framework 3.5 编译时,您可以充分利用 IDataErrorInfo 接口使 WPF 询问业务对象它们是否处于有效状态。这就不必在与业务层分离的对象中放置业务逻辑,并允许您创建独立于 UI 平台的业务对象。由于 IDataErrorInfo 接口已延续多年,这也使得您可以更容易地重新使用旧版 Windows 窗体或 ASP.NET 应用程序中的业务对象。
    假设您需要为范围以外的某个时间段提供验证,以确保用户的文本输入可转换为源属性的数据类型。时间段的起始日期不能为将来,因为我们无法了解尚不存在的时间段。要求某个时间段至少持续一毫秒也是有意义的。
    这些类型的规则与业务逻辑的一般观点相同,因为它们都是域规则的示例。最好在存储其状态的域对象中实现这些域规则。图 14 中所列的代码显示了 SmartEra 类,该类通过 IDataErrorInfo 接口公开验证错误消息。
    public class SmartEra 
        : System.ComponentModel.IDataErrorInfo
    {
        public DateTime StartDate { get; set; }
        public TimeSpan Duration { get; set; }
    
        #region IDataErrorInfo Members
    
        public string Error
        {
            get { return null; }
        }
    
        public string this[string property]
        {
            get 
            {
                string msg = null;
                switch (property)
                {
                    case "StartDate":
                        if (DateTime.Now < this.StartDate)
                            msg = "Start date must be in the past.";
                        break;
    
                    case "Duration":
                        if (this.Duration.Ticks == 0)
                            msg = "An era must have a duration.";
                        break;
    
                    default:
                        throw new ArgumentException(
                            "Unrecognized property: " + property);
                }
                return msg;
            }
        }
    
        #endregion // IDataErrorInfo Members
    }
    
    从 WPF 用户界面使用 SmartEra 类的验证支持非常简单。您唯一要做的就是告知绑定它们应使用其绑定对象上的 IDataErrorInfo 接口。您可以通过两种方法执行此操作,如图 15 所示。
    <!-- START DATE -->
    <TextBlock Grid.Row="0">Start Date:</TextBlock>
    <TextBox Grid.Row="1">
      <TextBox.Text>
        <Binding Path="StartDate" UpdateSourceTrigger="PropertyChanged">
          <Binding.ValidationRules>
            <ExceptionValidationRule />
            <DataErrorValidationRule />
          </Binding.ValidationRules>
        </Binding>
      </TextBox.Text>
    </TextBox>
    
    <!-- DURATION -->
    <TextBlock Grid.Row="2">Duration:</TextBlock>
    <TextBox 
      Grid.Row="3" 
      Text="{Binding 
             Path=Duration, 
             UpdateSourceTrigger=PropertyChanged, 
             ValidatesOnDataErrors=True,
             ValidatesOnExceptions=True}" 
      />
    
    与显式或隐式地向绑定的 ValidationRules 集合中添加 ExceptionValidationRule 相似,您可以将 DataErrorValidationRule 直接添加到绑定的 ValidationRules 中,也可以将 ValidatesOnDataErrors 属性设置为 true。两种方法所产生的实际效果相同;绑定系统会查询数据源的 IDataErrorInfo 接口是否有验证错误。

    结束语
    很多开发人员都喜欢 WPF 对数据绑定的丰富支持,这是有一定原因的。在 WPF 中使用绑定时功能如此强大、普及程度如此之深,很多软件开发人员因此不得不重新审视他们对数据与用户界面关系的看法。在诸多核心功能的协作下,WPF 可支持复杂的数据绑定方案,如模板、样式和附加属性。
    只要使用几行 XAML,您就可以表达想如何显示分层数据结构或如何验证用户输入。在高级环境下,您可以通过编程方式访问绑定系统以使用其全部功能。借助这样一款您可控制自如的强大基础结构,对于创建现代化业务应用程序的开发人员而言,创建出色的用户体验和引人注目的数据可视化这一长期目标触手可及

    转载于:https://www.cnblogs.com/wangzhuangye/archive/2012/02/05/2339140.html

    展开全文
  • 就我个人所得,目前用作WPF数据源的方法有如下几种: 1.控件的互相绑定; 2.XML存储数据; 3.序列化反序列化json串存储; ...其中控件的相互绑定...一、首先将数据存储在XML中 1 <?xml version="1.0" encodi...

    就我个人所得,目前用作WPF数据源的方法有如下几种:

    1.控件的互相绑定;

    2.XML存储数据;

    3.序列化反序列化json串存储;

    4.联系数据库;

     

    其中控件的相互绑定无非是binding方法,由于不能存储修改之后的操作,故在此不过多讨论;

    接下来说说XML方法存储数据;

     

    一、首先将数据存储在XML中

    1 <?xml version="1.0" encoding="utf-8" ?>
    2 <curriculum_info class_name = "高等数学" teacher = "吕良福" place ="24楼301室" time="31"></curriculum_info>

    例如一个名为curriculum_info的对象在courses.xml中;

     

    二、在XMAL中调用该对象中的数据

    在顶部(window下)需有如下代码:

     1     <Window.Resources>
     2         <XmlDataProvider x:Key="course-info" Source="courses.xml" XPath="/curriculum_info/*"></XmlDataProvider>
     3         <DataTemplate x:Key="showcourse">
     4             <TextBlock Text="{Binding XPath=class_name}"></TextBlock>
     5         </DataTemplate>
     6         <Style TargetType="{x:Type TextBlock}" >
     7             <Setter Property="FontStyle" Value="Italic" />
     8             <Setter Property="FontFamily" Value="Trebuchet MS" />
     9             <Setter Property="FontSize" Value="12" />
    10         </Style>
    11     </Window.Resources>
     
    对数据源的调用放在<Window.Resources>中。
      通过<XmlDataProvider>标记,实现对XML数据源的调用,x:Key是资源的引用的ID,Source是XML资源文件路径,XPath指出XML文件内部数据存放的路径。
     
    在<Window.Resources>还有另外两个标记,分别是DataTemplate和Style。
      DataTemplate中放置一个TextBlock用来,将TextBlock的Text属性与XML数据源中的class_name标记绑定,也就说用来显示XML文件中的class_name。
      Style是WPF的样式。在这里,我们指定了本Window中,所有的TextBlock都采用Style中定义的样式。
     
    接下来就可以实现绑定了:
    1 <TextBlock Grid.Column="1" Grid.Row="3">Course:<TextBlock DataContext="{Binding Source={StaticResource course-info}}" Text="{Binding XPath=class_name}" Width="26"/> </TextBlock>

    那么这个TextBlock中的Text就与XML中的curriculum_info的class_name绑定了;

     

    转载于:https://www.cnblogs.com/Elson8080/p/4477632.html

    展开全文
  • 可以 对用户进行数据的管理 用户之间的留言 等等 更有效的帮助您完成日常调配
  • wpfwpf数据的绑定验证

    千次阅读 2013-07-07 16:16:53
    使用数据绑定和 WPF 自定义数据显示 Josh Smith 本文将介绍以下内容: WPF 数据绑定 数据显示和分层数据 使用模板 输入验证 本文使用以下技术:  WPF、XAML、C# 代码下载...

    http://msdn.microsoft.com/zh-cn/magazine/cc700358.aspx

    使用数据绑定和 WPF 自定义数据显示
    Josh Smith
    本文将介绍以下内容:
    • WPF 数据绑定
    • 数据显示和分层数据
    • 使用模板
    • 输入验证
    本文使用以下技术: 
    WPF、XAML、C#

    代码下载位置: AdvancedWPFDatabinding2008_07.exe (171 KB) 
    在线浏览代码
     Windows® Presentation Foundation (WPF) 首次出现在 .NET 雷达上时,大多数文章和演示应用程序都对其华丽的渲染引擎和 3D 性能大加宣扬。这些示例虽然读起来引人入胜、玩起来趣味横生,但却无法证明 WPF 在现实世界中的强大功能。那些在单击后会突然放出烟火的三维旋转视频固然很酷,但我们当中的大多数人都不会用它创建应用程序。创建软件来显示和编辑大量复杂的业务或科学数据才是我们的衣食父母。
    让人振奋的是,WPF 为管理显示和编辑复杂数据提供了良好的支持。在 2007 年 12 月刊的《MSDN® 杂志上,John Papa 撰写了“WPF 中的数据绑定”一文 (msdn.microsoft.com/magazine/cc163299),其中对 WPF 数据绑定的重要概念做了出色的介绍。在此,我将以 John 在上述数据点专栏中讲到的内容为基础,探讨一些更高级的数据绑定方案。研究过这些方案后,您将了解到在大多数行业应用程序中达到常用数据绑定要求的各种方法。

    在代码中绑定
    WPF 为桌面应用程序开发人员带来的最大变化之一就是广泛地使用和支持声明性编程。WPF 用户界面和资源可通过使用基于 XML 的标准标记语言 - 可扩展应用程序标记语言 (XAML) 来声明。大多数 WPF 数据绑定的说明只展示了如何在 XAML 中使用绑定。由于在 XAML 中执行的所有操作都可在代码中实现,因此专业 WPF 开发人员学习如何以编程方式及声明方式来使用数据绑定就显得尤为重要。
    许多情况下,在 XAML 中声明绑定更为便利。随着系统变得更加复杂和更加动态,有时在代码中使用绑定更为适宜。在继续深入之前,让我们首先回顾一下编程式数据绑定中涉及到的一些常用的类和方法。
    WPF 元素从 FrameworkElement 或实体框架 ContentElement 那里同时继承了 SetBinding 和 GetBinding 表达式方法。它们恰好是在 BindingOperations 实用程序类中可调用相同名称方法的便捷方法。下列代码说明了如何使用 Binding­Operations 类将某个文本框的 Text 属性绑定到另一个对象的属性上:
    static void BindText(TextBox textBox, string property)
    {
          DependencyProperty textProp = TextBox.TextProperty;
          if (!BindingOperations.IsDataBound(textBox, textProp))
          {
              Binding b = new Binding(property);
              BindingOperations.SetBinding(textBox, textProp, b);
          }
    } 
    
    您可以使用此处显示的代码轻松地取消绑定某个属性:
    static void UnbindText(TextBox textBox)
    {
        DependencyProperty textProp = TextBox.TextProperty;
        if (BindingOperations.IsDataBound(textBox, textProp))
        {
            BindingOperations.ClearBinding(textBox, textProp);
        }
    }
    
    通过清除绑定,您还可以移除目标属性的绑定值。
    在 XAML 中声明数据绑定会隐藏某些基本细节。一旦您开始在代码中使用绑定,这些细节就会显现出来。其中之一就是绑定源与目标之间的关系实际上是由 BindingExpression 类(而不是 Binding 自身)维持的。Binding 类中包含多个 BindingExpressions 可共享的高级信息,但两个绑定属性之间的联系是由基础表达式决定的。下列代码说明了您如何使用 BindingExpression 以编程方式检查某个文本框的 Text 属性是否经过了验证:
    static bool IsTextValidated(TextBox textBox)
    {
        DependencyProperty textProp = TextBox.TextProperty;
    
        var expr = textBox.GetBindingExpression(textProp);
        if (expr == null)
            return false;
    
        Binding b = expr.ParentBinding;
        return b.ValidationRules.Any();
    } 
    
    由于 BindingExpression 不知道其是否已验证,您需要询问其父项绑定。稍后我将分析一下输入验证技术。
    虚拟实验室:高级 WPF 数据绑定
    WPF 为管理显示和编辑复杂数据提供了良好的支持。只要使用几行 XAML,您就可以显示分层数据结构或验证用户输入。您可以在我们预置的虚拟实验室中找到 WPF 数据绑定。所有内容都已安装完成,准备就绪,包括本文中介绍的项目。只需根据显示的内容开始实验和编码即可。

    在虚拟实验室中进行试验:

    使用模板
    如用户界面富有成效,它提供的原始数据可以使用户直观地从中发现有意义的信息。这就是数据可视化的本质。数据绑定就像一道数据可视化难题。几乎所有最繁琐的 WPF 程序都需要一种更有效的数据提供方式,而不是简单地将某个控件属性绑定到数据对象的某个属性上。真实的数据对象具有多个相关值,这些不同的值应合并为一个组织严密的直观表示。这就是 WPF 使用数据模板的原因。
    System.Windows.DataTemplate 类就是 WPF 中的一种模板。通常,模板就像 WPF 框架使用的一种“俗套”,它创建的可视元素协助呈现那些没有固有直观表示的对象。如某个元素尝试显示一个没有固有直观表示的对象(如自定义业务对象),您可以通过赋予 DataTemplate 来告知元素如何呈现该对象。
    DataTemplate 会根据需要尽可能多地产生可视元素来显示该数据对象。这些元素使用数据绑定显示该数据对象的属性值。如果某个元素不知道如何显示通知其呈现的对象,就会对其调用 ToString 方法并在 TextBlock 中显示结果。
    假定您有一个名为 FullName 的简单类,用于存储人名。您想显示一个姓名列表,并使每个人的姓氏突出显示。要执行此操作,您需要创建一个描述如何呈现 FullName 对象的 DataTemplate。图 1 中所列代码显示了 FullName 类以及将显示姓名列表的窗口的源代码。
    public class FullName
    {
        public string FirstName { get; set; }
        public char MiddleInitial { get; set; }
        public string LastName { get; set; }
    }
    
    public partial class WorkingWithTemplates : Window
    {
        // This is the Window's constructor.
        public WorkingWithTemplates()
        {
            InitializeComponent();
    
            base.DataContext = new FullName[]
            {
                new FullName 
                { 
                    FirstName = "Johann", 
                    MiddleInitial = 'S', 
                    LastName = "Bach" 
                },
                new FullName 
                { 
                    FirstName = "Gustav",
                    MiddleInitial = ' ',
                    LastName = "Mahler" 
                },
                new FullName 
                { 
                    FirstName = "Alfred", 
                    MiddleInitial = 'G', 
                    LastName = "Schnittke" 
                }
            };
        }
    }
    
    图 2 所示,窗口的 XAML 文件中含有一个 ItemsControl。它创建了一个用户无法选择或移除的项目列表。ItemsControl 向其 ItemTemplate 属性分配一个 DataTemplate,利用它来呈现在窗口构造函数中创建的各个 FullName 实例。您应该注意 DataTemplate 中的大部分 TextBlock 元素是如何将其 Text 属性绑定到了它们所代表的 FullName 对象的属性上的。
    <!-- This displays the FullName objects. -->
    <ItemsControl ItemsSource="{Binding Path=.}">
      <ItemsControl.ItemTemplate>
        <DataTemplate>
          <StackPanel Orientation="Horizontal">
            <TextBlock FontWeight="Bold" Text="{Binding LastName}" />
            <TextBlock Text=", " />
            <TextBlock Text="{Binding FirstName}" />
            <TextBlock Text=" " />
            <TextBlock Text="{Binding MiddleInitial}" />
          </StackPanel>
        </DataTemplate>
      </ItemsControl.ItemTemplate>
    </ItemsControl>
    
    当运行此演示应用程序时,其外观类似于图 3 所示。通过使用 DataTemplate 来呈现名称可以很容易地突出显示每个人的姓氏,因为相应的 TextBlock 的 FontWeight 均为粗体。这个简单的示例说明了 WPF 数据绑定与模板之间的基本关系。进一步探讨该主题时,我将会把这些功能组合成更强大的复杂对象可视化方法。
    图 3 由 DataTemplate 呈现的 FullNames

    使用继承的 DataContext
    默认情况下,所有绑定均隐式绑定在某个元素的 DataContext 属性上。因此,可以说是元素的 DataContext 引用了其数据源。在 DataContext 的工作方式上,需要了解一些特殊的内容。在您了解了 DataContext 这个微妙的方面后,将极大地简化复杂数据绑定用户界面的设计。
    并非必须设置元素的 DataContext 属性才能引用数据源对象。如果元素树(从技术上讲是逻辑树)中某个祖先元素的 DataContext 被赋值,则该值将自动被用户界面中的每个后代元素所继承。换言之,如果窗口的 DataContext 设置为引用 Foo 对象,则默认情况下,窗口中每个元素的 DataContext 都将引用同一 Foo 对象。您可以轻松地为窗口中的任何元素分配一个不同的 DataContext 值,这会使该元素的所有后代元素都将继承新的 DataContext 值。这与 Windows 窗体中的环境属性很相似。
    在上一部分中,我讨论了如何使用 DataTemplates 创建可视化数据对象。图 2 中由模板创建的元素将其属性绑定到某个 FullName 对象的属性。这些元素隐式地绑定到其 DataContext 上。由 DataTemplate 创建的元素的 DataContext 引用模板所使用的数据对象,如 FullName 对象。
    DataContext 属性的值继承中没有任何神奇之处。它只是利用了对 WPF 中内置继承依赖关系属性的支持。任何依赖关系属性都可以是继承属性,在向 WPF 的依赖关系属性系统注册该属性时,只需在提供的元数据中指定标记即可。
    继承依赖关系属性的另一个示例是 FontSize,它含有所有元素。如果您在窗口中设置了 FontSize 依赖关系属性,则默认情况下,该窗口中的所有元素都将以该大小显示其文本。沿元素树向下传播 FontSize 值所使用的基础结构与传播 DataContext 的基础结构相同。
    在面向对象环境中的“继承”表示子类继承其父类的成员,而这里使用的术语“继承”有所不同。属性值继承仅指值在运行时沿着元素树向下的传播。在面向对象含义中,类当然可以继承那些支持值继承的依赖关系属性。

    使用集合视图
    当 WPF 控件绑定到数据集合上时,它们不会直接绑定在集合本身上。而是隐式地绑定在自动封装该集合的视图上。该视图可实现 ICollectionView 界面,可以是若干具体实现之一,如 ListCollectionView。
    一个集合视图有多项职责。它可跟踪集合中的当前项,该项通常会转换为列表控件中的活动/选定项。集合视图还提供在列表内排序、筛选和归组项目的一般方法。可以围绕集合将多个控件绑定到同一视图上,以便它们形成彼此并列的关系。以下代码显示了 ICollectionView 的一些功能:
    // Get the default view wrapped around the list of Customers.
    ICollectionView view = CollectionViewSource.GetDefaultView(allCustomers);
    
    // Get the Customer selected in the UI.
    Customer selectedCustomer = view.CurrentItem as Customer;
    
    // Set the selected Customer in the UI.
    view.MoveCurrentTo(someOtherCustomer);
    
    所有列表控件(如列表框、组合框和列表视图)必须将它们的 IsSynchronizedWithCurrentItem 属性设置为 true,以与集合视图的 Current­Item 属性保持同步。抽象的 Selector 类定义了该属性。如果未设置为 true,选择列表控件中的某项将不会更新该集合视图的 CurrentItem,向 CurrentItem 分配新值也不会在列表控件中有所反映。

    使用分层数据
    现实生活中到处都是分层数据。客户下达多重订单、分子由很多个原子组成、部门由多名员工组成,太阳系包含一系列的天体。您肯定对这种常见的主从复合排列非常熟悉。
    WPF 提供了多种使用分层数据结构的方法,分别适用于不同的情形。从本质上看,这是在选择使用多个控件显示数据,还是在一个控件中显示多层数据。接下来,我就要讲述这两种方法。

    使用多个控件显示 XML 数据
    一种极为常见的分层数据处理方法就是利用单独的控件显示各个层级。例如,假设我们有一个表示客户、订单和订单详细信息的系统。在此情况下,我们可能需要一个组合框来显示客户,用列表框显示所有选定的客户订单,然后用 ItemsControl 显示所选订单的相关详细信息。这是一种显示分层数据的不错的方法,极易在 WPF 中实现。
    根据我之前描述的场景,图 4 显示了某个数据的简化示例,它封装在 WPF XmlDataProvider 组件中,可供应用程序处理。一个类似于图 5 的用户界面将显示该数据。注意客户和订单是可选的,而订单的详细信息是存在于只读列表中的。这一点非常有意义,因为可视对象应仅在影响应用程序的状态时或处于可编辑状态时才可供选择。
    <XmlDataProvider x:Key="xmlData">
      <x:XData>
        <customers >
          <customer name="Customer 1">
            <order desc="Big Order">
              <orderDetail product="Glue" quantity="21" />
              <orderDetail product="Fudge" quantity="32" />
              </order>
              <order desc="Little Order">
                <orderDetail product="Ham" quantity="1" />
                <orderDetail product="Yarn" quantity="2" />
              </order>
            </customer>
            <customer name="Customer 2">
              <order desc="First Order">
                <orderDetail product="Mousetrap" quantity="4" />
              </order>
            </customer>
          </customers>
        </x:XData>
    </XmlDataProvider>
    
    图 5 显示 XML 数据的一种方法
    图 6 中的 XAML 介绍了如何使用这些不同的控件来显示刚才所示的分层数据。此窗口不需要任何代码,它完全存在于 XAML 中。
    <Grid DataContext=
        "{Binding Source={StaticResource xmlData},
        XPath=customers/customer}"
        Margin="4" 
      >
      <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <RowDefinition />
        <RowDefinition />
      </Grid.RowDefinitions>
    
      <!-- CUSTOMERS -->
      <DockPanel Grid.Row="0">
        <TextBlock DockPanel.Dock="Top" FontWeight="Bold" Text="Customers" />
        <ComboBox
          IsSynchronizedWithCurrentItem="True"
          ItemsSource="{Binding}"
          >
          <ComboBox.ItemTemplate>
            <DataTemplate>
              <TextBlock Text="{Binding XPath=@name}" />
            </DataTemplate>
          </ComboBox.ItemTemplate>
        </ComboBox>
      </DockPanel>
    
      <!-- ORDERS -->
      <DockPanel Grid.Row="1">
        <TextBlock DockPanel.Dock="Top" FontWeight="Bold" Text="Orders" />
        <ListBox
          x:Name="orderSelector" 
          DataContext="{Binding Path=CurrentItem}"
          IsSynchronizedWithCurrentItem="True" 
          ItemsSource="{Binding XPath=order}"
          >
          <ListBox.ItemTemplate>
            <DataTemplate>
              <TextBlock Text="{Binding XPath=@desc}" />
            </DataTemplate>
          </ListBox.ItemTemplate>
        </ListBox>
      </DockPanel>
    
      <!-- ORDER DETAILS -->
      <DockPanel Grid.Row="2">
        <TextBlock DockPanel.Dock="Top" FontWeight="Bold" 
          Text="Order Details" />
        <ItemsControl 
          DataContext=
             "{Binding ElementName=orderSelector, Path=SelectedItem}"
          ItemsSource="{Binding XPath=orderDetail}">
          <ItemsControl.ItemTemplate>
            <DataTemplate>
              <TextBlock>
                <Run>Product:</Run>
                <TextBlock Text="{Binding XPath=@product}" />
                <Run>(</Run>
                <TextBlock Text="{Binding XPath=@quantity}" />
                <Run>)</Run>
              </TextBlock>
            </DataTemplate>
          </ItemsControl.ItemTemplate>
        </ItemsControl>
      </DockPanel>
    </Grid>
    
    注意,在这里广泛使用了短 XPath 查询通知 WPF 在哪里获取绑定值。绑定类向您提供 XPath 属性,您可以为其分配 XmlNode.SelectNodes 方法支持的任何 XPath 查询。实质上,WPF 是使用该方法来执行 XPath 查询的。遗憾的是,这意味着由于 XmlNode.SelectNodes 当前不支持使用 XPath 函数,WPF 数据绑定也不支持它们。
    客户组合框以及订单列表框都绑定到 Xpath 查询(由根网格的 DataContext 绑定执行)的合成节点集上。列表框的 DataContext 将自动返回集合视图的 CurrentItem,该集合视图封装为网格的 DataContext 生成的 XmlNodes 集合。换言之,列表框的 DataContext 是当前选定的 Customer。由于该列表框的 ItemsSource 隐式地绑定到其自己的 DataContext(因为没有指定任何其他源)上,且其 ItemsSource 绑定会执行 XPath 查询以从 DataContext 中获取 <order> 元素,因此 ItemsSource 将有效地绑定到选定客户的订单列表上。
    请记住,在绑定到 XML 数据时,您实际上是绑定到由对 XmlNode.SelectNodes 的调用创建的对象上。如果不仔细,您就会最终将多个控件绑定到逻辑上等效但实际不同的多组 XmlNodes 上。这是由于每次调用 XmlNode.SelectNodes 都会生成一组新的 XmlNodes,即使您每次都是将相同的 XPath 查询传递给同一 XmlNode 也是如此。这是需要对 XML 数据绑定特别注意的一点,在绑定业务对象时,可将其忽略。

    使用多个控件显示业务对象
    假设您现在要绑定到上一示例的同一数据中,但数据以业务对象而不是 XML 的形式存在。这会对您绑定数据结构各个层次的方法有何影响?使用的技术会有何相同或不同之处?
    图 7 中的代码显示了一个简单类,它用于创建业务对象,其中存储着我们将要绑定的数据。这些类构成的逻辑架构与前一部分中 XML 数据所使用的架构相同。
    public class Customer
    {
        public string Name { get; set; }
        public List<Order> Orders { get; set; }
    
        public override string ToString()
        {
            return this.Name;
        }
    }
    
    public class Order
    {
        public string Desc { get; set; }
        public List<OrderDetail> OrderDetails { get; set; }
    
        public override string ToString()
        {
            return this.Desc;
        }
    }
    
    public class OrderDetail
    {
        public string Product { get; set; }
        public int Quantity { get; set; }
    }
    
    图 8 所示为这些对象显示窗口的 XAML。它与图 6 中看到的 XAML 很相似,但其中一些重要的差异需要注意。XAML 中没有显示出是该窗口的构造函数创建了数据对象并设置了 DataContext,而不是 XAML 将其作为资源进行引用。请注意,其中没有任何控件显式设置了 DataContext。它们全部都继承了同一个 DataContext,该 DataContext 是一个 List<Customer> 实例。
    <Grid Margin="4">
      <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <RowDefinition />
        <RowDefinition />
      </Grid.RowDefinitions>
    
       <!-- CUSTOMERS -->
       <DockPanel Grid.Row="0">
         <TextBlock DockPanel.Dock="Top" FontWeight="Bold" Text="Customers"
         />
         <ComboBox 
           IsSynchronizedWithCurrentItem="True" 
           ItemsSource="{Binding Path=.}" 
           />
       </DockPanel>
    
       <!-- ORDERS -->
       <DockPanel Grid.Row="1">
         <TextBlock DockPanel.Dock="Top" FontWeight="Bold" Text="Orders" />
         <ListBox 
           IsSynchronizedWithCurrentItem="True" 
           ItemsSource="{Binding Path=CurrentItem.Orders}" 
           />
       </DockPanel>
    
       <!-- ORDER DETAILS -->
       <DockPanel Grid.Row="2">
         <TextBlock DockPanel.Dock="Top" FontWeight="Bold" 
            Text="Order Details" />
         <ItemsControl
           ItemsSource="{Binding Path=CurrentItem.Orders.CurrentItem.
           OrderDetails}"
           >
           <ItemsControl.ItemTemplate>
             <DataTemplate>
               <TextBlock>
                 <Run>Product:</Run>
                 <TextBlock Text="{Binding Path=Product}" />
                 <Run>(</Run>
                 <TextBlock Text="{Binding Path=Quantity}" />
                 <Run>)</Run>
               </TextBlock>
             </DataTemplate>
           </ItemsControl.ItemTemplate>
         </ItemsControl>
       </DockPanel>
    </Grid>
    
    绑定到业务对象(而非 XML)时,另一个明显的区别是,承载订单详细信息的 ItemsControl 不需要绑定到订单列表框的 SelectedItem。该方法在 XML 绑定中是必要的,因为对于列表项来自本地 XPath 查询的列表,没有任何通用的方式可以用来引用它的当前项。
    在绑定到业务对象(而不是 XML)时,通常是按照所选项的嵌套级别来绑定的。ItemsControl 的 ItemsSource 绑定利用了这个便捷的特点,在绑定路径中两次指定 CurrentItem: 一次是针对所选客户,一次是针对所选订单。如前文所述,CurrentItem 属性是封装数据源的基础 ICollectionView 的成员。
    就 XML 和业务对象之间在工作方式上的差异而言,还有一点需要注意。由于 XML 示例绑定到 XmlElements,因此必须提供 DataTemplates 以解释如何呈现客户和订单。在绑定到自定义业务对象时,只需覆盖 Customer 类和 Order 类的 ToString 方法,并允许 WPF 为这些对象显示该方法的输出即可避免这种开销。这一窍门仅够处理具有简单文本表示的对象。在处理复杂的数据对象时,使用这种便捷的技术可能不适用。

    一个用于显示整个层次结构的控件
    到现在为止,您只了解了通过在不同的控件中显示层次结构的每个层级来展示分层数据的方法。但通常需要在同一控件中显示分层数据的所有层级,这对数据处理是有帮助的,也是必要的。此方法的典型例子就是 TreeView 控件,该控件支持显示和浏览任意层级的嵌套数据。
    您可通过以下两种方式用各项来填充 WPF TreeView。一种方法以代码或 XAML 方式手动添加项,另一种方法是通过数据绑定创建这些项。
    下面的 XAML 表明了如何通过 XAML 将一些 TreeViewItem 手动添加到 TreeView:
    <TreeView>
      <TreeViewItem Header="Item 1">
        <TreeViewItem Header="Sub-Item 1" />
        <TreeViewItem Header="Sub-Item 2" />
      </TreeViewItem>
      <TreeViewItem Header="Item 2" />
    </TreeView>
    
    如控件始终都显示小型静态项组,在 TreeView 中手动创建项的方法是很实用的。如果需要显示大量会随时间变化的数据,就必须使用更具动态性的方法。此时,您有两种方法可供选择。您可以编写这样的代码:从头至尾检查整个数据结构,根据找到的数据对象创建 TreeViewItem,然后将这些项添加到 TreeView。或者利用分层数据模板,让 WPF 代您完成所有工作。

    使用分层数据模板
    您可以用声明的方式解释 WPF 应如何通过分层数据模板呈现分层数据。利用 HierarchicalDataTemplate 类这一工具可以弥补复杂数据结构与该数据的直观表示之间的缺口。它与常用 DataTemplate 非常相似,但还允许您指定数据对象子项的来源。您还可以为 HierarchicalDataTemplate 提供一个用于呈现这些子项的模板。
    假定您现在要在一个 TreeView 控件中显示图 7 中展现的数据。该 TreeView 控件看上去可能有些类似于图 9。实现此控件需要使用两个 HierarchicalDataTemplate 和一个 DataTemplate。
    图 9 在 TreeView 中显示整个数据层次结构
    这两个分层模板可显示 Customer 对象和 Order 对象。由于 OrderDetail 对象没有任何子项,您可以通过非分层 DataTemplate 呈现这些对象。TreeView 的 ItemTemplate 属性会将该模板用于 Customer 类型的对象,因为 Customer 是包含在 TreeView 根层级下的数据对象。图 10 中所列的 XAML 表明了如何将所有这些方式有机地结合在一起。
    <Grid>
      <Grid.DataContext>
        <!-- 
        This sets the DataContext of the UI
        to a Customers returned by calling
        the static CreateCustomers method. 
        -->
        <ObjectDataProvider 
          xmlns:local="clr-namespace:VariousBindingExamples"
          ObjectType="{x:Type local:Customer}"
          MethodName="CreateCustomers"
          />
      </Grid.DataContext>
    
      <Grid.Resources>
        <!-- ORDER DETAIL TEMPLATE -->
        <DataTemplate x:Key="OrderDetailTemplate">
          <TextBlock>
            <Run>Product:</Run>
            <TextBlock Text="{Binding Path=Product}" />
            <Run>(</Run>
            <TextBlock Text="{Binding Path=Quantity}" />
            <Run>)</Run>
          </TextBlock>
        </DataTemplate>
    
        <!-- ORDER TEMPLATE -->
        <HierarchicalDataTemplate 
          x:Key="OrderTemplate"
          ItemsSource="{Binding Path=OrderDetails}"
          ItemTemplate="{StaticResource OrderDetailTemplate}"
          >
          <TextBlock Text="{Binding Path=Desc}" />
        </HierarchicalDataTemplate>
    
        <!-- CUSTOMER TEMPLATE -->
        <HierarchicalDataTemplate 
          x:Key="CustomerTemplate"
          ItemsSource="{Binding Path=Orders}"
          ItemTemplate="{StaticResource OrderTemplate}"
          >
          <TextBlock Text="{Binding Path=Name}" />
        </HierarchicalDataTemplate>
      </Grid.Resources>
    
      <TreeView
        ItemsSource="{Binding Path=.}"
        ItemTemplate="{StaticResource CustomerTemplate}"
        />
    
    </Grid>
    
    我为 Grid(包含 TreeView )的 DataContext 分配了一个 Customer 对象集合。这在 XAML 中通过使用 ObjectDataProvider 即可实现,它是从 XAML 调用方法的一种便捷方式。由于 DataContext 是在元素树中自上而下地继承,因此 TreeView 的 DataContext 会引用这组 Customer 对象。这就是我们可以为其 ItemsSource 属性提供一个 "{Binding Path=.}" 绑定的原因,通过这种方式可以表明 ItemsSource 属性被绑定到 TreeView 的 DataContext。
    如果没有分配 TreeView 的 ItemTemplate 属性,则 TreeView 将仅显示顶层的 Customer 对象。由于 WPF 不知道如何呈现 Customer,因此它会对每个 Customer 都调用 ToString,并为每项都显示该文本。它无法确定每个 Customer 都有一个与其关联的 Order 对象列表,且每个 Order 都有一个 OrderDetail 对象列表。由于 WPF 无法理解您的数据架构,因此您必须向 WPF 解释架构,使它能正确呈现数据结构。
    向 WPF 解释数据的结构和外观是 HierarchicalDataTemplates 的份内工作。此演示中所使用的模板包含的可视元素树非常简单,就是其中带有少量文本的 TextBlocks。在更复杂的应用程序中,模板可能包含交互式的旋转 3D 模型、图像、矢量绘图、复杂的 UserControl 或任何其他可视化基础数据对象的 WPF 内容。
    需要特别注意声明模板的顺序。必须先声明一个模板后才能通过 StaticResource 扩展对其进行引用。这是由 XAML 阅读器规定的要求,该要求适用于所有资源,而不仅仅是模板。
    或者可通过使用 DynamicResource 扩展来引用模板,在这种情况下,模板声明的词汇顺序无关紧要。但使用 DynamicResource 引用(而不是 StaticResource 引用)总会带来一些运行时开销,因为它们要监控资源系统的更改。由于我们不会在运行时替换模板,因此这笔开销是多余的,最好使用 StaticResource 引用并恰当地安排模板声明的顺序。

    使用用户输入
    对大多数程序而言,显示数据仅仅是这场较量的一半。另一个巨大的挑战是分析、接受和拒绝用户输入的数据。理想状态下,所有用户都始终输入符合逻辑且准确的数据,那么这会是一项简单的任务。但在现实生活中,却根本不是这么回事。现实中的用户会出现打字错误、忘记输入所需的值、在错误的位置输入值、删除不应删除的记录、添加本不该有的记录,人都会犯错误。
    作为开发人员和架构师,我们的工作就是与无法避免的错误和恶意用户输入作斗争。WPF 绑定基础结构支持输入验证。在本文接下来的几节中,我将讲述如何充分利用 WPF 对验证的支持,以及如何向用户显示验证错误消息。

    通过 ValidationRules 验证输入
    WPF 的第一个版本包含在 Microsoft® .NET Framework 3.0 之内,只能有限地支持输入验证。Binding 类具有 ValidationRules 属性,它可存储任意数量的 ValidationRule 派生类。这些规则中的每一个都包含一些可通过测试查看绑定值是否有效的逻辑。
    那时,WPF 仅随一个被称为 ExceptionValidationRule 的 ValidationRule 子类出现。开发人员可将该规则添加到绑定的 ValidationRules 中,它将捕捉在数据源更新过程中抛出的异常,从而允许 UI 显示异常的错误消息。这种输入验证方法的有效性极富争议,人们认为良好用户体验的基础是避免不必要地向用户泄露技术细节。对于大多数用户而言,数据分析异常的错误消息通常过于专业,对不起,这个话题有点离题。
    假设您有一个表示时间段的类,例如这里看到的简单 Era 类:
    public class Era
    {
        public DateTime StartDate { get; set; }
        public TimeSpan Duration { get; set; }
    }
    
    如果您希望允许用户编辑某个时间段的开始日期和持续时间,您可以使用两个文本框控件,然后将它们的 Text 属性绑定到 Era 实例的属性上。由于用户可在文本框内输入任何他想输入的文本,您无法确保输入的文本可转换为 DateTime 或 TimeSpan 的实例。在这种情况下,您可以使用 ExceptionValidationRule 报告数据转换错误,然后在用户界面上显示这些转换错误。图 11 中所列的 XAML 展示了完成此任务的方式。
    <!-- START DATE -->
    <TextBlock Grid.Row="0">Start Date:</TextBlock>
    <TextBox Grid.Row="1">
      <TextBox.Text>
        <Binding Path="StartDate" UpdateSourceTrigger="PropertyChanged">
          <Binding.ValidationRules>
            <ExceptionValidationRule />
          </Binding.ValidationRules>
        </Binding>
      </TextBox.Text>
    </TextBox>
    
    <!-- DURATION -->
    <TextBlock Grid.Row="2">Duration:</TextBlock>
    <TextBox 
      Grid.Row="3" 
      Text="{Binding 
             Path=Duration, 
             UpdateSourceTrigger=PropertyChanged, 
             ValidatesOnExceptions=True}" 
      />
    
    这两个文本框展示了在 XAML 中将 ExceptionValidationRule 添加到绑定的 ValidationRules 中的两种方法。“开始日期”文本框使用详细的属性元素语法显式地添加规则。“持续时间”文本框使用简写语法,将绑定的 ValidatesOnExceptions 属性设置为 true。这两类绑定都将其 UpdateSourceTrigger 属性设置为 PropertyChanged,这样每次为文本框的 Text 属性赋新值时都会进行验证,而不是空等控件。该程序的屏幕快照如图 12 所示。
    图 12 ExceptionValidationRule 显示验证错误

    显示验证错误
    图 13 所示,“Duration”(持续时间)文本框中包含一个无效值。其包含的字符串无法转换为 TimeSpan 实例。该文本框的工具提示显示了一则错误消息,控件的右侧出现一个小的红色错误图标。这种行为并不会自动发生,但很容易实现和自定义。
    <!-- 
    The template which renders a TextBox 
    when it contains invalid data. 
    -->
    <ControlTemplate x:Key="TextBoxErrorTemplate">
      <DockPanel>
        <Ellipse 
          DockPanel.Dock="Right" 
          Margin="2,0"
          ToolTip="Contains invalid data"
          Width="10" Height="10"   
          >
          <Ellipse.Fill>
            <LinearGradientBrush>
              <GradientStop Color="#11FF1111" Offset="0" />
              <GradientStop Color="#FFFF0000" Offset="1" />
            </LinearGradientBrush>
          </Ellipse.Fill>
        </Ellipse>
        <!-- 
        This placeholder occupies where the TextBox will appear. 
        -->
        <AdornedElementPlaceholder />
      </DockPanel>
    </ControlTemplate>
    
    <!-- 
    The Style applied to both TextBox controls in the UI.
    -->
    <Style TargetType="TextBox">
      <Setter Property="Margin" Value="4,4,10,4" />
      <Setter 
        Property="Validation.ErrorTemplate" 
        Value="{StaticResource TextBoxErrorTemplate}" 
        />
      <Style.Triggers>
        <Trigger Property="Validation.HasError" Value="True">
          <Setter Property="ToolTip">
            <Setter.Value>
              <Binding 
                Path="(Validation.Errors)[0].ErrorContent"
                RelativeSource="{x:Static RelativeSource.Self}"
                />
            </Setter.Value>
          </Setter>
        </Trigger>
      </Style.Triggers>
    </Style>
    
    静态 Validation 类通过使用某些附加的属性和静态方法在控件与其包含的任何验证错误之间形成关系。您可以在 XAML 中引用这些附加属性,就用户界面向用户提供输入验证错误的方法创建说明,将其做为标记。图 13 中的 XAML 负责解释如何呈现上一个示例中两个文本框控件的输入错误消息。
    图 13 中 Style 的目标是 UI 中文本框的所有实例。它对文本框应用了三种设置。第一个 Setter 影响文本框的 Margin 属性。Margin 属性可设置为适当的值,以提供足够的空间在右侧显示错误图标。
    Style 中的下一个 Setter 分配 ControlTemplate,在包含无效数据时它负责呈现文本框。它将附加的 Validation.ErrorTemplate 属性设置为在 Style 之上声明的 ControlTemplate。当 Validation 类报告文本框有一处或多处验证错误时,该文本框将随该模板一同呈现。这里就是红色错误图标的来源,如图 12 所示。
    Style 还包含一个用于监控文本框上附加的 Validation.HasError 属性的 Trigger。当 Validation 类为文本框将附加的 HasError 属性设置为 true 时,Style 的 Trigger 激活并向文本框分配一条工具提示。工具提示的内容绑定为异常的错误消息,该异常在尝试将文本框的文本解析为源属性的数据类型实例时抛出。

    通过 IDataErrorInfo 验证输入
    在引入 Microsoft .NET Framework 3.5 后,WPF 对输入验证的支持得到了显著改进。ValidationRule 方法对于简单的应用程序很有用,但现实中的应用程序需要处理复杂的真实数据和业务规则。将业务规则编码到 ValidationRule 对象中不仅仅是将代码捆绑到 WPF 平台上,还不允许业务逻辑在其所属位置:业务对象中存在!
    很多应用程序都有一个业务层,该层的一组业务对象中包含复杂的业务处理规则。针对 Microsoft .NET Framework 3.5 编译时,您可以充分利用 IDataErrorInfo 接口使 WPF 询问业务对象它们是否处于有效状态。这就不必在与业务层分离的对象中放置业务逻辑,并允许您创建独立于 UI 平台的业务对象。由于 IDataErrorInfo 接口已延续多年,这也使得您可以更容易地重新使用旧版 Windows 窗体或 ASP.NET 应用程序中的业务对象。
    假设您需要为范围以外的某个时间段提供验证,以确保用户的文本输入可转换为源属性的数据类型。时间段的起始日期不能为将来,因为我们无法了解尚不存在的时间段。要求某个时间段至少持续一毫秒也是有意义的。
    这些类型的规则与业务逻辑的一般观点相同,因为它们都是域规则的示例。最好在存储其状态的域对象中实现这些域规则。图 14 中所列的代码显示了 SmartEra 类,该类通过 IDataErrorInfo 接口公开验证错误消息。
    public class SmartEra 
        : System.ComponentModel.IDataErrorInfo
    {
        public DateTime StartDate { get; set; }
        public TimeSpan Duration { get; set; }
    
        #region IDataErrorInfo Members
    
        public string Error
        {
            get { return null; }
        }
    
        public string this[string property]
        {
            get 
            {
                string msg = null;
                switch (property)
                {
                    case "StartDate":
                        if (DateTime.Now < this.StartDate)
                            msg = "Start date must be in the past.";
                        break;
    
                    case "Duration":
                        if (this.Duration.Ticks == 0)
                            msg = "An era must have a duration.";
                        break;
    
                    default:
                        throw new ArgumentException(
                            "Unrecognized property: " + property);
                }
                return msg;
            }
        }
    
        #endregion // IDataErrorInfo Members
    }
    
    从 WPF 用户界面使用 SmartEra 类的验证支持非常简单。您唯一要做的就是告知绑定它们应使用其绑定对象上的 IDataErrorInfo 接口。您可以通过两种方法执行此操作,如图 15 所示。
    <!-- START DATE -->
    <TextBlock Grid.Row="0">Start Date:</TextBlock>
    <TextBox Grid.Row="1">
      <TextBox.Text>
        <Binding Path="StartDate" UpdateSourceTrigger="PropertyChanged">
          <Binding.ValidationRules>
            <ExceptionValidationRule />
            <DataErrorValidationRule />
          </Binding.ValidationRules>
        </Binding>
      </TextBox.Text>
    </TextBox>
    
    <!-- DURATION -->
    <TextBlock Grid.Row="2">Duration:</TextBlock>
    <TextBox 
      Grid.Row="3" 
      Text="{Binding 
             Path=Duration, 
             UpdateSourceTrigger=PropertyChanged, 
             ValidatesOnDataErrors=True,
             ValidatesOnExceptions=True}" 
      />
    
    与显式或隐式地向绑定的 ValidationRules 集合中添加 ExceptionValidationRule 相似,您可以将 DataErrorValidationRule 直接添加到绑定的 ValidationRules 中,也可以将 ValidatesOnDataErrors 属性设置为 true。两种方法所产生的实际效果相同;绑定系统会查询数据源的 IDataErrorInfo 接口是否有验证错误。

    结束语
    很多开发人员都喜欢 WPF 对数据绑定的丰富支持,这是有一定原因的。在 WPF 中使用绑定时功能如此强大、普及程度如此之深,很多软件开发人员因此不得不重新审视他们对数据与用户界面关系的看法。在诸多核心功能的协作下,WPF 可支持复杂的数据绑定方案,如模板、样式和附加属性。
    只要使用几行 XAML,您就可以表达想如何显示分层数据结构或如何验证用户输入。在高级环境下,您可以通过编程方式访问绑定系统以使用其全部功能。借助这样一款您可控制自如的强大基础结构,对于创建现代化业务应用程序的开发人员而言,创建出色的用户体验和引人注目的数据可视化这一长期目标触手可及。

    展开全文
  • wpf数据绑定

    2013-05-14 23:25:00
    一、WPF数据绑定的概要 数据绑定:是应用程序 UI 与业务逻辑之间建立连接的过程。 如果绑定正确设置并且数据提供正确通知,则当数据的值发生更改时,绑定到数据的视觉元素会自动反映更改。 数据绑定可能还意味着...

    一、WPF数据绑定的概要

    数据绑定:是应用程序 UI 与业务逻辑之间建立连接的过程。 如果绑定正确设置并且数据提供正确通知,则当数据的值发生更改时,绑定到数据的视觉元素会自动反映更改。 数据绑定可能还意味着如果视觉元素中数据的外部表现形式发生更改,则基础数据可以自动更新以反映更改。

    例如:如果用户编辑 TextBox 元素中的值,则基础数据值会自动更新以反映该更改。

     

    1. 数据绑定涉及到两个方面:

    一个是绑定源,一个是绑定目标。绑定源即控件绑定所使用的源数据,绑定目标即数据显示的控件。

    2. 对于绑定源,在WPF可以是以下四种:

    • CLR对象:可以绑定到CLR类的公开的属性、子属性、索引器上。
    • ADO.Net对象:例如DataTable、DataView等 。
    • XML文件:使用XPath进行解析 。
    • DependencyObject:绑定到其依赖项属性上,即控件绑定控件 。

    对于绑定目标,必须是WPF中的DependencyObject,将数据绑定到其依赖项属性上。

     

    二、      绑定的模式

    1.  根据数据流的方向,WPF中的数据绑定分为以下四种:

    OneWay 绑定:对源属性的更改会自动更新目标属性,但是对目标属性的更改不会传播回源属性。此绑定类型适用于绑定的控件为隐式只读控件的情况。

    TwoWay 绑定:对源属性的更改会自动更新目标属性,而对目标属性的更改也会自动更新源属性。此绑定类型适用于可编辑窗体或其他完全交互式 UI 方案 。

    OneWayToSource 与 OneWay 相反;它在目标属性更改时更新源属性。

    OneTime绑定:该绑定会导致源属性初始化目标属性,但不传播后续更改。

    注释:如果无需监视目标属性的更改,则使用 OneWay 绑定模式可避免 TwoWay 绑定模式的系统开销。

    大多数属性都默认为 OneWay 绑定,但是一些依赖项属性,通常为用户可编辑的控件的属性,如 TextBox 的 Text 属性和 CheckBox 的 IsChecked 属性,默认为 TwoWay 绑定。

    如果要知道依赖项属性绑定在默认情况下是单向还是双向的编程方法可使用 GetMetadata 获取属性的属性元数据,然后检查 BindsTwoWayByDefault 属性的布尔值。

     1 <Page x:Class="WpfDemo.Page1"
     2 
     3     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
     4 
     5     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
     6 
     7     Title="Page1" HorizontalAlignment="Center">
     8 
     9         <Grid Name="GridTable" Height="360" Background="Silver">
    10 
    11             <Grid.RowDefinitions>
    12 
    13                 <RowDefinition></RowDefinition>
    14 
    15                 <RowDefinition></RowDefinition>
    16 
    17                 <RowDefinition></RowDefinition>
    18 
    19                 <RowDefinition></RowDefinition>
    20 
    21             </Grid.RowDefinitions>
    22 
    23             <Grid.ColumnDefinitions>
    24 
    25             <ColumnDefinition Width="130"></ColumnDefinition>
    26 
    27             <ColumnDefinition Width="150"></ColumnDefinition>
    28 
    29             <ColumnDefinition Width="20"></ColumnDefinition>
    30 
    31             </Grid.ColumnDefinitions>
    32 
    33         <Label Width="130" Height="25"  Grid.Row="0" Grid.Column="0"  Name="label1">TwoWay</Label>
    34 
    35         <TextBox Width="150" Height="25"  Grid.Row="0" Grid.Column="1"  Name="textBox4" Text="{Binding ElementName=scrollBar1,Path=Value,Mode=TwoWay}" />
    36 
    37         <Label Width="130" Height="25"  Grid.Row="1" Grid.Column="0"  Name="label2">OneWay</Label> 
    38 
    39         <TextBox Width="150" Height="25"  Grid.Row="1" Grid.Column="1"   Name="textBox1" Text="{Binding ElementName=scrollBar1, Path=Value,Mode=OneWay}"/>
    40 
    41         <Label Width="130" Height="25"  Grid.Row="2" Grid.Column="0"  Name="label3">OneWayToSource</Label>
    42 
    43         <TextBox Width="150" Height="25"  Grid.Row="2" Grid.Column="1"   Name="textBox2" Text="{Binding ElementName=scrollBar1, Path=Value,Mode=OneWayToSource}" />
    44 
    45         <Label Width="130" Height="25"  Grid.Row="3" Grid.Column="0"  Name="label4">OneTime</Label> 
    46 
    47         <TextBox Width="150" Height="25"  Grid.Row="3" Grid.Column="1"   Name="textBox3" Text="{Binding ElementName=scrollBar1, Path=Value,Mode=OneTime}"/>
    48 
    49          <ScrollBar Value="30" Minimum="0" Grid.RowSpan="4" Grid.Row="0" Grid.Column="2" Maximum="100" Name="scrollBar1" Width="18" Height="{Binding ElementName=GridTable,Path=Height}" />
    50 
    51         </Grid>
    52 
    53 </Page>
    xmal代码

    根据程序执行结果,我们可以得到以下结论:

    对于OneWay绑定:在界面中显示的数据可以随数据源的值的变化而变化,但更改界面的数据不会影响到数据源。

    对于TwoWay绑定:界面中显示的数据及数据源的数据可以双向显示及更新。

    对于OneWayToSource绑定:初始时界面的数据为空;更改界面的数据可以影响数据源的值,但是更改数据源的值不会体现在界面上。

    对于OneTime绑定:在界面中显示的为数据源的初始值,更改数据源的值的时候,不会更改界面的数据显示;更改界面的数据也不会影响到数据源的数据。

     

    三、绑定目标值影响绑定源值条件

    问题:绑定源的值是在您编辑文本的同时进行更新,还是在您结束编辑文本并将鼠标指针从文本框移走后才进行更新呢?或者在您需要更新的情况下在手动的更新呢?

    1. UpdateSourceTrigger 属性是确定触发源更新的原因。

    下图中右箭头的点演示 UpdateSourceTrigger 属性的角色:

    TwoWay及OneWayToSource是由绑定目标到绑定源方向,若实现绑定目标的值更改影响绑定源的值方式,只需要设置相应控件绑定时的UpdateSourceTrigger的值,其值有三种:

    PropertyChanged:当绑定目标属性更改时,立即更新绑定源。

    LostFocus:当绑定目标元素失去焦点时,更新绑定源。

    Explicit:仅在调用 UpdateSource 方法时更新绑定源。

    注释:多数依赖项属性的UpdateSourceTrigger 值的默认值为 PropertyChanged,而 Text 属性的默认值为 LostFocus。

    四、      数据提供程序

    1. XmlDataProvider:

    XmlDataProvider访问 XML 数据的方式有以下三种:

    可以使用 XmlDataProvider 类嵌入内联 XML 数据。

    可以将 Source 属性设置为 XML 数据文件的 Uri

    可以将 Document 属性设置为 XmlDocument。

    注释:当 XmlDocument.NodeChanged 事件发生时,XmlDataProvider 执行所有绑定的完全刷新。 特定节点不进行优化。

    默认情况下,XmlDataProvider.IsAsynchronous 属性设置为 true,表示默认情况下 XmlDataProvider 检索数据并异步生成 XML 节点的集合。

    以下将介绍使用上面所述的三种方式显示xml数据:

     1 <Page x:Class="WpfDemo.xmlBinding"
     2 
     3     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
     4 
     5     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
     6 
     7     Title="xmlBinding" xmlns:local="clr-namespace:WpfDemo">
     8 
     9     <Page.Resources>
    10 
    11         <XmlDataProvider x:Key="XmlFile" Source="Students.xml" XPath="/Students"></XmlDataProvider>
    12 
    13         <XmlDataProvider x:Key="InnerXmlStu" XPath="/Students">
    14 
    15             <x:XData>
    16 
    17                 <Students xmlns="">
    18 
    19                     <Student><name>swd</name></Student>
    20 
    21                     <Student><name>awd</name></Student>
    22 
    23                     <Student><name>asd</name></Student>
    24 
    25                 </Students>
    26 
    27             </x:XData>
    28 
    29         </XmlDataProvider>
    30 
    31     </Page.Resources>
    32 
    33     <Grid>
    34 
    35         <Grid.RowDefinitions>
    36 
    37             <RowDefinition></RowDefinition>
    38 
    39             <RowDefinition></RowDefinition>
    40 
    41             <RowDefinition></RowDefinition>
    42 
    43         </Grid.RowDefinitions>
    44 
    45         <Grid.ColumnDefinitions>
    46 
    47             <ColumnDefinition Width="100"></ColumnDefinition>
    48 
    49             <ColumnDefinition Width="150"></ColumnDefinition>
    50 
    51         </Grid.ColumnDefinitions>
    52 
    53         <TextBlock Grid.Row="0" Grid.Column="0"  Height="25" Width="100"  Text="引用XML文件"></TextBlock>
    54 
    55         <TextBlock Grid.Row="1" Grid.Column="0" Height="25" Width="100"   Text="内嵌XML"></TextBlock>
    56 
    57         <TextBlock Grid.Row="2" Grid.Column="0"  Height="25" Width="100"  Text="动态XML"></TextBlock>
    58 
    59         <ListBox Name="lisbXmlFile" Grid.Row="0" Grid.Column="1" Height="100" Width="150" ItemsSource="{Binding Source={StaticResource XmlFile},XPath=Student/name}">
    60 
    61         </ListBox>
    62 
    63         <ListBox Name="lisbInnerXml" Grid.Row="1" Grid.Column="1"  Height="100" Width="150" ItemsSource="{Binding Source={StaticResource InnerXmlStu},XPath=Student/name}">
    64 
    65         </ListBox>
    66 
    67         <ListBox Name="lisbXmlDoc" Grid.Row="2" Grid.Column="1"  Height="100"  Width="150" ItemsSource="{Binding XPath=Student/name}">
    68 
    69         </ListBox>
    70 
    71     </Grid>
    72 
    73 </Page>
    xmal代码

    2. ObjectDataProvider:

    ObjectDataProvider 使您能够在 XAML 中创建可用作绑定源的对象,并为您提供以下属性,以对对象执行查询并绑定到结果。

    使用 ConstructorParameters 属性将参数传递给对象的构造函数。

    使用 MethodName 属性调用一个方法。

    使用 MethodParameters 属性将参数传递给该方法。 然后,可以绑定到该方法的结果。

    使用ObjectType 指定将提供数据绑定源的对象。

    使用 ObjectInstance 属性来指定现有的对象实例作为源

    注释:还可以使用 IsAsynchronous 属性指定是在辅助线程还是在活动上下文中执行对象创建。也就是是否异步检索数据。

    五、类型转换与数据校验

    1. IValueConverter接口

    提供一种将自定义逻辑应用于绑定的方式。

    在Binding时,数据源对象到目标对象之间(或者目标对象到数据源对象)可能需要某种转换。这时只需实现IValueConverter接口自定义值转换器即可。

    接口原型定义:

    public interface IValueConverter
    {
        object Convert(object value, Type targetType, object parameter, CultureInfo culture);
        object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture);
    }

    参数value是要转换的值,typeTarget是转换后的值类型,parameter是Binding 类的 ConverterParameter传递过来的参数。

    Convert方法:数据绑定引擎在将值从绑定源传播给绑定目标时,调用此方法。

    ConvertBack方法:数据绑定引擎在将值从绑定目标传播给绑定源时,调用此方法。

    ValueConversion属性作用是告诉自定义转换器类可以转换的源数据和目标数据的 类型(ValueConversion属性将在稍后的示例中看到)。

    2. ValidationRule类

    提供一种为检查用户输入的有效性而创建自定义规则的方法。

    ValidationRule : 所有自定义验证规则的基类。提供了让用户定义验证规则的入口。

    ExceptionValidation :表示一个规则,该规则检查在绑定源属性更新过程中引发的异常。它是一个内置的规则,它检查在绑定源属性更新过程中引发的异常。

    ValidationResult : 数据验证结果的表现方式。ValidationRule对象的Validate方法执行完毕后通过ValidationResult来表示验证的结果。这里包含了错误信息—ErrorContent,数据是否有效—IsValid。ValidResult 为 ValidationResult 的有效实例。

    ValidationError :表示一个验证错误,该错误在 ValidationRule 报告验证错误时由绑定引擎创建。

    六、      绑定集合对象

    1.       ICollectionView接口

    允许集合具有当前记录管理、自定义排序、筛选和分组这些功能。比如排序,分组,筛选,导航以及其它自定义视图,并且这不会影响到你的后台数据的实际存储。

    2.       ObservableCollection <T> 类

    表示一个动态数据集合,在添加项、移除项或刷新整个列表时,此集合将提供通知。

    3.       WPF MVVM概要

    MVVM(Model-View-ViewModel)是由MVC,MVP演变而来。MVVM分离了逻辑与界面,解放业务逻辑。

    转载于:https://www.cnblogs.com/nghygaojun/archive/2013/05/14/3078947.html

    展开全文
  • WPF数据绑定

    2013-03-02 22:03:00
    一、WPF数据绑定的概要 数据绑定:是应用程序 UI 与业务逻辑之间建立连接的过程。 如果绑定正确设置并且数据提供正确通知,则当数据的值发生更改时,绑定到数据的视觉元素会自动反映更改。 数据绑定可能还意味着...
  • 使用数据绑定和 WPF 自定义数据显示

    千次阅读 2009-11-10 17:07:00
    当 Windows® Presentation Foundation (WPF) 首次出现在 .NET 雷达上时,大多数文章和演示应用程序都对其华丽的渲染引擎和 3D 性能大加宣扬。这些示例虽然读起来引人入胜、玩起来趣味横生,但却无法证明 WPF 在现实...
  • 本次文献内容打算分享一下近期所学的新知识点,关于WPF的一些方法,先从基本的数据表格查询方法开始吧。查询的方法不止一个,可以在wpf里面写,也可以在数据库里面写,这次我要分享的是在数据库写查询方法,使用的是...
  • 数据绑定:是应用程序 UI 与业务逻辑之间建立连接的过程。 如果绑定正确设置并且数据...一、WPF数据绑定的概要 数据绑定:是应用程序 UI 与业务逻辑之间建立连接的过程。 如果绑定正确设置并且数据提供正确通知,...
  • WPF数据绑定笔记摘录

    千次阅读 2012-03-31 00:17:10
    所谓数据绑定,其概念很简单,数据绑定决定了一个Source的改变会不会,以及怎样自动通知并改变Destination。 Source可以是数据表,可以是XML,可以是一个内存数据,也可以是一个控件的某个属性值。 Destination...
  • WPF:数据虚拟化

    2009-04-06 17:42:00
    下载源代码介绍从UI的观点上看,WPF为有效的处理大型集合提供了一些...背景UI虚拟化 当一个WPF的ItemControl被绑定到一个大型集合的数据源时,如果可以UI虚拟化,该控件将只为那些在可以看到的项创见可视化的容器(...
  • WPF数据绑定

    千次阅读 2009-12-17 16:13:00
    本文来自MSDN, 版权归微软所有http://msdn.microsoft.com/zh-cn/library/aa480224.aspx Windows Presentation Foundation 数据绑定:第一部分 发布日期 : 2006-6-5 | 更新日期 : 2006-6-5适用于:Microsoft ...
  • WPF 数据绑定(上)

    千次阅读 2013-08-26 23:57:59
    我们已经了解到使用WPF可以设计强大的用户界面,那么用户界面和后台逻辑之间的数据传递是如何实现的呢,这里就使用到WPF中的数据绑定功能。这也是WPF比较核心的部分。数据绑定功能的优势包括 ,包括本质上支持数据...
  • 19 WPF 数据绑定

    2013-04-20 10:28:00
    WPF数据绑定允许你创造从几乎任何对象的任何属性获取信息以及填充到几乎任何元素的任何属性里的绑定。 使用自定义对象绑定到数据库 建立数据访问组件 数据绑定是将数据对象绑定到界面元素,首先建立访问数据库的...
  • WPF中对数据的处理

    2021-01-20 17:03:32
    首先,WPF是微软推出的基于Windows 的用户界面框架, 属于.NET Framework 3.0的一部分。它提供了统一的编程模型、语言和框架,真正做到了 分离界面设计人员与开发人员的工作;同时它提供了全新的多媒体交互用户图形...
  • 21 WPF数据视图

    2013-06-18 15:02:00
    这些特征独立于数据对象本身,意味着你能以不同的方式、在窗口的不同部分(或应用的不同部分)绑定相同的数据。例如,你能绑定相同的产品集合到两个不同的列表但是过滤他们显示不同的记录。 视图对象依赖于数据对象...
  • 参数是用户id revenue_today 今日收益 revenue_contacts 人脉收益 balance 可用余额 sql部分如下: { call p_my_wallet( #{userId,jdbcType=INTEGER,mode=IN} ) } 则mapper部分为: Map getMyWallet(@Param(...
  • WPF XAML 单表删除数据

    2019-05-16 22:45:47
    删除数据是一件可大可小的事情,肯定有提示框提示提醒用户是否要把数据,删除了数据就删除了,单表删除数据没有那么多的条件约束用户删除数据,只是提示框提示问用户确定要删除数据吗? 1、在SQL Server 找到对应的...
  • WPF删除多条数据

    2020-04-27 23:42:48
    选中要删除的数据,如图 ...IF(@type = ‘Delete_LogManagement’) --删除用户操作(日志管理) BEGIN DELETE SYS_ConLog WHERE ConLogID=@ConLogID//表的ID END 服务器的代码 [OperationContract]//删除 ...
  • WPF 使用 XmlDataProvider 提供数据

    千次阅读 2021-02-04 03:07:17
    HBase作为NoSQL的一员,在性能上不如memcached和redis,但是持久化存储方面比内存NoSQL强。作为文档型NoSQL在分布式存储上比mongo做sharding和MapReduce分析方便多。 HBase是一个分布式存储、数据库...
  • 本文基本属于转贴(在More Databinding and Custom Controls基础上做了少量改写,出于学习的目的^_^... 定义一个Users类,用以存储多个用户; 定义一个UserView控件,用以格式化显示每个用户; 在最终的页面上通...
  • WPF存储过程

    2021-06-19 09:51:43
    存储过程命名习惯:跟窗口名保持一致。ALTER PROCEDURWE [dbo].[WD_Login] 声明变量初始化值要注意;字符串varchar、char、日期类型datetime等的初始值;数值double、decimal ,整形int等初始值0。@type char(200)=’...
  • 当为一个集合(通常绑定在DataGrid或其它ItemsControl控件)添加或编辑一个项时,通常会弹出一个编辑界面编辑项的属性,编辑结束再提交,或者我们不想编辑数据了,此时选择取消,数据项的内容没有任何改变。...
  • -创建三个用于用户管理的视图-用户数据:UserId,LoginName,密码,姓,FirstName,BirthDate,BirthPlace,AddressCity -将数据存储在文件中 登录视图:用户/密码(在文件中检查) 用户列表视图:网格中的用户,...
  • 数据绑定是通过System.Windows.Data.Binding对象来实现,它能够将2个属性绑定在一起,在接下来的应用程序生命周期中让绑定来负责所有的同步工作。 1.1 在代码中实现 绑定代码 Binding binding = new Binding(); //...
  • 录制内容,从上位机应用基础架构出发,全程代码实战,涉及内容包括串口通信、基础组件开发、用户控件动画、全局静态数据绑定等等。从无到有,完整实操,项目整体以MVVM思想模式设计开发,代码功能使用分层结构,逻辑...
  • 本文演示了在WPF上具有通用lambda表达式的筛选器模式。筛选器模式与管道一起在数据驱动的应用程序中大量使用。使用C#lambda表达式,可以缩短代码并使代码通用化。 管道和过滤器模式 传统上,为每个新过滤器实现...
  • wpf

    2011-05-12 07:28:00
    WPF 体系结构 .NET Framework 4 其他版本 .NET Framework 3.5 .NET Framework 3.0 本主题提供 Windows Presentation Foundation (WPF) 类层次结构的指导教程, 涵盖了 ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 7,365
精华内容 2,946
关键字:

wpf用户数据存储