Start at 2013/8/3 16:27:21


WPF资源(Resource)(1)

一、什么是资源

通常使用 WPF 资源作为重用通常定义的对象和值的简单方法。例如定义一种可以复用的单色的Brush对象,按钮的背景及矩形的填充颜色均使用此Brush:

  <Window x:Class="WPFResource.WinBasicResource"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      Title="Basic Resource" Height="200" Width="300">
      <Window.Resources>
          <SolidColorBrush x:Key="myBrush" Color="Gold" />
      </Window.Resources>
      <StackPanel>
          <Button Margin="5" Content="Sample Button" Background="{StaticResource myBrush}" />
         <Rectangle Margin="5" Width="100" Height="100" Fill="{StaticResource myBrush}" />
     </StackPanel>
 </Window>

在WPF中资源通常用作“样式”(Style)、样式模板、数据模板等。

二、资源的定义及XAML中引用

资源可以定义在以下几个位置:

  • 应用程序级资源:定义在App.xaml文件中,作为整个应用程序共享的资源存在

    在App.xaml文件中定义:

      <Application x:Class="WPFResource.App"
          xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
          xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
          StartupUri="Window1.xaml">
          <Application.Resources>
              <SolidColorBrush Color="Gold" x:Key="myGoldBrush" />
          </Application.Resources>
      </Application>
    
  • 窗体级资源:定义在Window或Page中,作为一个窗体或页面共享的资源存在

    <Window x:Class="WPFResource.WindowResourceDemo"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      Title="WindowResourceDemo" Height="300" Width="300">
      <Window.Resources>
          <SolidColorBrush x:Key="myRedBrush" Color="Red" />
      </Window.Resources>
      <StackPanel>
          <Button Margin="5" Background="{StaticResource myRedBrush}">Sample Button</Button>
     </StackPanel>
    </Window>
    
  • 文件级资源:定义在资源字典的XAML文件中,再引用 在Visual Studio的WPF应用程序项目中,添加“资源字典(Resource Dictionary)”类型的项

    在其XAML文件中定义:

      <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
          xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
          <SolidColorBrush x:Key="myWhiteBrush" Color="White" />
      </ResourceDictionary>
    

    在FileResourceDemo.xaml文件(窗体)中,将其注册为窗体级的资源,并引用

    <Window x:Class="WPFResource.FileResourceDemo"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      Title="FileResourceDemo" Height="300" Width="300">
      <Window.Resources>
          <ResourceDictionary Source="MyResourceDictionary.xaml" />
      </Window.Resources>
      <StackPanel>
          <Button Margin="5" Background="{StaticResource myWhiteBrush}">Sample Button</Button>
     </StackPanel>
    </Window>
    
  • 对象(控件)级资源:定义在某个ContentControl中,作为其子容器、子控件共享的资源

    在Button中定义一个资源,供Button内的Content控件使用

    <Window x:Class="WPFResource.ControlResourceDemo"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      Title="ControlResourceDemo" Height="300" Width="300">
      <StackPanel>
          <Button Margin="5">
              <Button.Resources>
                  <SolidColorBrush x:Key="myGreenBrush" Color="Green" />
              </Button.Resources>
             <Button.Content>
                 <TextBlock Text="Sample Text" Background="{StaticResource myGreenBrush}" />
             </Button.Content>
         </Button> 
     </StackPanel>
    </Window>
    

三、XAML解析资源的顺序

在XAML中解析资源按照由引用资源的控件向外层容器依次调用资源。例如在在应用程序级别、窗体级别及对象级别分为定义x:Key相的同资源:

在App.xaml文件中:

  <Application x:Class="WPFResource.App"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      StartupUri="MultiResourceReference.xaml">
      <Application.Resources>
          <!-- 应用程序级资源 -->
          <SolidColorBrush Color="Gold" x:Key="myGoldBrush" />
          <SolidColorBrush Color="Blue" x:Key="myBrush" />
      </Application.Resources>
 </Application>

在窗体的XAML文件中:

  <Window x:Class="WPFResource.MultiResourceReference"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      Title="MultiResourceReference" Height="265" Width="300">
      <Window.Resources>
          <!-- 窗体级资源 -->
          <SolidColorBrush Color="White" x:Key="myWhiteBrush" />
          <SolidColorBrush Color="Green" x:Key="myBrush" />
      </Window.Resources>
     <StackPanel>
         <!-- 使用应用程序级定义的资源 -->
         <Button Margin="5" Content="Sample Button" Background="{StaticResource myGoldBrush}" />

         <!-- 使用窗体级定义的资源 -->
         <Button Margin="5" Content="Sample Button" Background="{StaticResource myWhiteBrush}" />

         <!-- 窗体级资源的值覆盖应用程序级资源的值 -->
         <Button Margin="5" Content="Sample Button" Background="{StaticResource myBrush}" />

         <StackPanel Background="#FF999999">
             <StackPanel.Resources>
                 <!-- 对象级资源 -->
                 <SolidColorBrush Color="Yellow" x:Key="myYellowBrush" />
                 <SolidColorBrush Color="Red" x:Key="myBrush" />
             </StackPanel.Resources>

             <!-- 使用应用程序级定义的资源 -->
             <Button Margin="5" Content="Sample Button" Background="{StaticResource myGoldBrush}" />

             <!-- 使用窗体级定义的资源 -->
             <Button Margin="5" Content="Sample Button" Background="{StaticResource myWhiteBrush}" />

             <!-- 使用对象级定义的资源 -->
             <Button Margin="5" Content="Sample Button" Background="{StaticResource myYellowBrush}" />

             <!-- 使用对象级定义的资源覆盖窗体级、应用程序级定义的资源 -->
             <Button Margin="5" Content="Sample Button" Background="{StaticResource myBrush}" />
         </StackPanel>
     </StackPanel>
 </Window>

WPF资源(Resource)(2)

四、静态资源(StaticResource)和动态资源(DynamicResource)

资源可以作为静态资源或动态资源进行引用。这是通过使用 StaticResource 标记扩展或 DynamicResource 标记扩展完成的。

通常来说,不需要在运行时更改的资源使用静态资源;而需要在运行时更改的资源使用动态资源。动态资源需要使用的系统开销大于静态资源的系统开销。

  • 静态资源引用是从控件所在的容器开始依次向上查找的,而动态资源的引用是从控件开始向上查找的(即控件的资源覆盖其父容器的同名资源)
  • 更改资源时,动态引用的控件样式发生变化(即"Dynamic Resource Button A"发生变化)

静态资源引用最适合于以下情况:

  • 您的应用程序设计几乎将所有的应用程序资源集中到页或应用程序级别的资源字典中。静态资源引用不会基于运行时行为(例如重新加载页)进行重新求值,因此,根据您的资源和应用程序设计避免大量不必要的动态资源引用,这样可以提高性能。
  • 您正在设置不在 DependencyObject 或 Freezable 上的属性的值。
  • 您正在创建将编译为 DLL 并打包为应用程序的一部分或在应用程序之间共享的资源字典。
  • 您正在为自定义控件创建一个主题,并定义在主题中使用的资源。对于这种情况,通常不需要动态资源引用查找行为,而需要静态资源引用行为,以使该查找可预测并且独立于该主题。使用动态资源引用时,即使是主题中的引用也会直到运行时才进行求值,并且在应用主题时,某个本地元素有可能会重新定义您的主题试图引用的键,并且本地元素在查找中会位于主题本身之前。如果发生该情况,主题将不会按预期方式运行。
  • 您正在使用资源来设置大量依赖项属性。依赖项属性具有由属性系统启用的有效值缓存功能,因此,如果您为可以在加载时求值的依赖项属性提供值,该依赖项属性将不必查看重新求值的表达式,并且可以返回最后一个有效值。该方法具有性能优势。
  • 您需要为所有使用者更改基础资源,或者需要通过使用 x:Shared 属性为每个使用者维护独立的可写实例。

动态资源最适合于以下情况:

  • 资源的值取决于直到运行时才知道的情况。这包括系统资源,或用户可设置的资源。例如,您可以创建引用由 SystemColors、SystemFonts 或 SystemParameters 公开的系统属性的 setter 值。这些值是真正动态的,因为它们最终来自于用户和操作系统的运行时环境。您还可以使用可以更改的应用程序级别的主题,在此情况下,页级别的资源访问还必须捕获更改。
  • 您正在为自定义控件创建或引用主题样式。
  • 您希望在应用程序生存期调整 ResourceDictionary 的内容。
  • 您有一个存在依存关系的复杂资源结构,在这种情况下,可能需要前向引用。静态资源引用不支持前向引用,但动态资源引用支持,因为资源直到运行时才需要进行求值,因此,前向引用不是一个相关概念。
  • 从编译或工作集角度来说,您引用的资源特别大,并且加载页时可能无法立即使用该资源。静态资源引用始终在加载页时从 XAML 加载;而动态资源引用直到实际使用时才会加载。
  • 您要创建的样式的 setter 值可能来自受主题或其他用户设置影响的其他值。
  • 您正在将资源应用到元素,而在应用程序生存期中可能会在逻辑树中重新设置该元素的父级。更改此父级还可能会更改资源查找范围,因此,如果您希望基于新范围对重新设置了父级的元素的资源进行重新求值,请始终使用动态资源引用。

五、不同类型的资源

  1. 程序集资源。这种常见于将图片设定到程序集中,做为程序集的资源。

    程序集资源在定义时,将文件复制到解决方案-项目所在的目录或其子目录中,并将文件的属性中的Build Action设置为Resource。(注意,WPF不支持项目属性中的资源)。

    此种方法适用于较小的资源。

  2. 对象资源

    除刚刚我们使用的图片做为程序集资源外,前面例子中所使用的资源均是对象资源。系统对于对象资源使用ResouceDictionary这个字典集合处理,其Key对应即x:Key声明的键,Value对应资源。


WPF样式(Style)与模板(Template)

一、WPF样式

类似于Web应用程序中的CSS,在WPF中可以为控件定义统一的样式(Style)。样式属于资源的一种,例如为Button定义统一的背景颜色和字体:

<Window.Resources>
      <Style 
          TargetType="Button">
          <Setter Property="Background" Value="Yellow" />
          <Setter Property="Margin" Value="5" />
          <Setter Property="FontFamily" Value="Comic Sans MS"/>
          <Setter Property="FontSize" Value="14"/>
      </Style>
  </Window.Resources>
 <StackPanel>
     <Button>Button A</Button>
     <Button Foreground="Red" Background="White">Button B</Button>
 </StackPanel>
  • 在Style中定义的属性及值,影响到Window中的所有类型为Button的控件的样式
  • 在Button中可以新定义其他属性(如Foreground),覆盖Style中的定义(Background)

这种样式,类似于CSS中的类型选择器,为某种类型定义样式。

此外还可以在Style中加入x:Key属性,做为特定的样式(注意,这种也需要定义TargetType);定义时还可以基于已定义的某种样式

<Window.Resources>
      <Style 
          TargetType="Button">
          <Setter Property="Background" Value="Yellow" />
          <Setter Property="Margin" Value="5" />
          <Setter Property="FontFamily" Value="Comic Sans MS"/>
          <Setter Property="FontSize" Value="14"/>
      </Style>
      <Style
         TargetType="Button"
         x:Key="ButtonStyleA"
         BasedOn="{StaticResource {x:Type Button}}">
         <Setter Property="Background" Value="Green" />
         <Setter Property="Foreground" Value="Yellow" />
         <Setter Property="FontSize" Value="28"/>
     </Style>
 </Window.Resources>
 <StackPanel>
     <Button>Button A</Button>
     <Button Foreground="Red" Background="White">Button B</Button>
     <Button Style="{StaticResource ButtonStyleA}">Button C</Button>
     <Button Style="{StaticResource ButtonStyleA}" Content="Button D">
         <Button.Foreground>
             <LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">
                 <LinearGradientBrush.GradientStops>
                     <GradientStop Offset="0.0" Color="#FFFFFF" />
                     <GradientStop Offset="1.0" Color="#0000FF" />
                 </LinearGradientBrush.GradientStops>
             </LinearGradientBrush>
         </Button.Foreground>
     </Button>
 </StackPanel>

二、控件模板(ControlTemplate)

当使用一个控件时,如果控件的属性、方法、事件满足程序的需求,但控件的外观不满足要求的时候,除了自定义控件这种方法外,我们还可以通过使用“控件模板”的方式更改控件的外观。

<Window.Resources>
      <Style TargetType="Button" x:Key="ButtonStyle">
          <!--设置按钮的默认的样式-->
          <Setter Property="FontFamily" Value="Comic Sans MS"/>
          <Setter Property="FontSize" Value="14"/>
          <Setter Property="Foreground" Value="Black" />
          <Setter Property="Background">
              <Setter.Value>
                  <LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">
                     <LinearGradientBrush.GradientStops>
                         <GradientStop Offset="0.0" Color="#fff" />
                         <GradientStop Offset="1.0" Color="#009" />
                     </LinearGradientBrush.GradientStops>
                 </LinearGradientBrush>
             </Setter.Value>
         </Setter>
         <!--设置按钮的模板-->
         <Setter Property="Template">
             <Setter.Value>
                 <ControlTemplate TargetType="Button">
                     <Grid>
                         <Ellipse Fill="{TemplateBinding Background}"/>
                         <ContentPresenter
                             Margin="5"
                             HorizontalAlignment="Center"
                             VerticalAlignment="Center"/>
                     </Grid>
                 </ControlTemplate>
             </Setter.Value>
         </Setter>
     </Style>
 </Window.Resources>
 <StackPanel>
     <Button Margin="5" Style="{StaticResource ButtonStyle}" 
             Width="100" Height="100" 
             Content="My Button">
     </Button>
     <Button Margin="5" Width="200">Common Button</Button>
 </StackPanel>

三、触发器

值得注意的是,这个时候,对于此按钮,无论是否获得焦点、鼠标是处于其上方,显示的外观均是相同的,如果要定义以上的一些效果,可以使用触发器来实现。

Style、ControlTemplate 和 DataTemplate 都具有 Triggers 属性,该属性可以包含一组触发器。某个属性值更改时,或某个事件引发时,触发器会相应地设置属性或启动操作(如动画操作)。

触发器包含以下几种:

  • 属性触发器
  • EventTrigger 和 Storyboard
  • MultiTrigger、DataTrigger 和 MultiDataTrigger

我们这里可以使用属性触发器来实现:

  <ControlTemplate.Triggers>
      <Trigger Property="IsMouseOver" Value="True">
          <!--鼠标在上移动时-->
          <Setter Property="Foreground" Value="Yellow" />
      </Trigger>
      <Trigger Property="IsKeyboardFocused" Value="True">
          <!--控件获得键盘焦点时-->
          <Setter Property="Foreground" Value="White" />
      </Trigger>
 </ControlTemplate.Triggers>

WPF更换主题

如果要做到一个应用程序其基本的内容不变,但改变整个应用程序的外观可以这样做:

  • 对于每一套外观定义一个ResourceDictionary
  • 在应用程序中,动态加载此应用程序(或窗体)的Resource

主题样式A的ResourceDictionary的XAML文件内容:

  <ResourceDictionary
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
      <!-- Canvas样式 -->
      <Style TargetType="Canvas">
          <Setter Property="Background">
              <Setter.Value>
                  <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                      <GradientStop Color="#FFFCF6F6" Offset="0"/>
                     <GradientStop Color="#FF201999" Offset="1"/>
                 </LinearGradientBrush>
             </Setter.Value>
         </Setter>
     </Style>
 </ResourceDictionary>

样式B、样式C的XAML代码与其类似,在这里省略。

窗体应用程序的XAML及CS代码如下:

  <Window x:Class="StyleAndTemplete.TheMeDemo"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      Title="TheMe Demo" Height="223" Width="319" ResizeMode="NoResize" Loaded="Window_Loaded">
      <Canvas>
          <Image Width="150" Height="150" 
                 Canvas.Left="129" Canvas.Top="20"
                 x:Name="userImage"/>
          <TextBlock Width="78" Height="20" 
                    Text="Name:" TextWrapping="Wrap" 
                    Canvas.Left="12" Canvas.Top="11"/>
         <ComboBox Width="97" Height="27" 
                   IsSynchronizedWithCurrentItem="True" 
                   Canvas.Left="12" Canvas.Top="37"
                   x:Name="userName" SelectionChanged="userName_SelectionChanged" />
     </Canvas>
 </Window>
  using System;
  using System.Windows;
  using System.Windows.Controls;
  using System.Windows.Media.Imaging;

  namespace StyleAndTemplete
  {
      /// <summary>
      /// Interaction logic for TheMeDemo.xaml
     /// </summary>
     public partial class TheMeDemo : Window
     {
         public TheMeDemo()
         {
             InitializeComponent();
         }

         private void Window_Loaded(object sender, RoutedEventArgs e)
         {
             userName.Items.Add("Patrick");
             userName.Items.Add("Abbey");
             userName.Items.Add("Tobey");

             userName.SelectedIndex = 0;
         }

         private void userName_SelectionChanged(object sender, SelectionChangedEventArgs e)
         {
             string selectedTitle = userName.SelectedItem.ToString();
             string imgName = string.Format("/TitleImage/tile_{0}.png", selectedTitle);

             BitmapImage img = new BitmapImage(
                 new Uri(imgName,UriKind.Relative));

             userImage.Source = img;

             string dicName = string.Format(
                 "{0}Resource.xaml", selectedTitle);

             this.Resources = (ResourceDictionary)(Application.LoadComponent(
                 new Uri(dicName,UriKind.Relative)));
         }
     }
 }

WPF数据绑定概述

WPF数据绑定为应用程序提供了一种表示数据和与数据交互的简单而又一致的方法。元素能够以公共语言运行库 (CLR) 对象和 XML 的形式绑定到各种数据源中的数据。

一、数据绑定的基本概念:

数据绑定涉及到两个方面:一个是绑定源,再一个是绑定目标。绑定源即控件绑定所使用的源数据,绑定目标即数据显示的控件。

  1. 对于绑定源,在WPF可以是以下四种:
    • CLR对象:可以绑定到CLR类的公开的属性、子属性、索引器上
    • ADO.Net对象:例如DataTable、DataView等
    • XML文件:使用XPath进行解析
    • DependencyObject:绑定到其依赖项属性上,即控件绑定控件
  2. 对于绑定目标,必须是WPF中的DependencyObject,将数据绑定到其依赖项属性上。

二、绑定的基本方式

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

  • OneWay 绑定导致对源属性的更改会自动更新目标属性,但是对目标属性的更改不会传播回源属性。此绑定类型适用于绑定的控件为隐式只读控件的情况。例如,您可能绑定到如股票行情自动收录器这样的源,或许目标属性没有用于进行更改的控件接口(如表的数据绑定背景色)。如果无需监视目标属性的更改,则使用 OneWay 绑定模式可避免 TwoWay 绑定模式的系统开销。

  • TwoWay 绑定导致对源属性的更改会自动更新目标属性,而对目标属性的更改也会自动更新源属性。此绑定类型适用于可编辑窗体或其他完全交互式 UI 方案。大多数属性都默认为 OneWay 绑定,但是一些依赖项属性(通常为用户可编辑的控件的属性,如 TextBox 的 Text 属性和 CheckBox 的 IsChecked 属性)默认为 TwoWay 绑定。确定依赖项属性绑定在默认情况下是单向还是双向的编程方法是:使用 GetMetadata 获取属性的属性元数据,然后检查 BindsTwoWayByDefault 属性的布尔值。

  • OneWayToSource 与 OneWay 绑定相反;它在目标属性更改时更新源属性。一个示例方案是您只需要从 UI 重新计算源值的情况。

  • OneTime绑定 ,该绑定会导致源属性初始化目标属性,但不传播后续更改。这意味着,如果数据上下文发生了更改,或者数据上下文中的对象发生了更改,则更改会反映在目标属性中。如果您使用的数据的当前状态的快照适于使用,或者这些数据是真正静态的,则适合使用此绑定类型。如果要使用源属性中的某个值初始化目标属性,并且事先不知道数据上下文,则也可以使用此绑定类型。此绑定类型实质上是 OneWay 绑定的简化形式,在源值不更改的情况下可以提供更好的性能。

每个依赖项属性的默认值都不同。一般情况下,用户可编辑控件属性(例如文本框和复选框的属性)默认为双向绑定,而多数其他属性默认为单向绑定。确定依赖项属性绑定在默认情况下是单向还是双向的编程方法是:使用 GetMetadata 来获取属性的属性元数据,然后检查 BindsTwoWayByDefault 属性的布尔值。

三、实现数据源更改影响目标更改

如果要实现数据源更改时,改变目标的值(即上图中的OneWay方式及TwoWay方式的由绑定源到绑定目标方向的数据绑定),需使数据源对象实现System.ComponentModel命名空间的INotifyPropertyChanged接口。INotifyPropertyChanged接口中定义了一个PropertyChanged事件,在某属性值发生变化时引发此事件,即可通知绑定目标更改其显示的值。例如:

  using System.ComponentModel;

  namespace BasicWPFDataBinding
  {
      public class MyData : INotifyPropertyChanged
      {
          #region INotifyPropertyChanged Members
          public event PropertyChangedEventHandler PropertyChanged;
          #endregion

         public MyData()
         {
             Name = "Tom";
         }

         private string _Name;
         public string Name
         {
             set
             {
                 _Name = value;

                 if (PropertyChanged != null)
                 {
                     // 引发PropertyChanged事件,
                     // PropertyChangedEventArgs构造方法中的参数字符串表示属性名
                     PropertyChanged(this,new PropertyChangedEventArgs("Name"));
                 }
             } 
             get
             {
                 return _Name;
             }
         }
     }
 }

四、实现绑定目标的值更改影响绑定源的值

若实现实现绑定目标的值更改影响绑定源的值(即上图中TwoWay的由绑定目标到绑定源方向,及OneWayToSource),可以设置相应控件绑定时的UpdateSourceTrigger的值,其值有三种:

  • PropertyChanged:当绑定目标属性更改时,立即更新绑定源。
  • LostFocus:当绑定目标元素失去焦点时,更新绑定源。
  • Explicit:仅在调用 UpdateSource 方法时更新绑定源。

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

例如:

  <TextBox Text="{Binding Path=Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
  • 对于OneTime绑定:在界面中显示的为数据源的初始值,更改数据源的值的时候,不会更改界面的数据显示;更改界面的数据也不会影响到数据源的数据。

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

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

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


数据绑定值的自定义转换

对于数据绑定,绑定的数据源的值类型和绑定目标的依赖属性的值类型可能会不同,系统提供了一些默认的绑定类型转换,另外也可以由用户自定义这种绑定转换:

一、定义CLR类型

定义一个CLR类型,内部存在两个属性字符串类型的ColorString和Color对象类型的ColorObject,供应用程序界面调用:

  using System.ComponentModel;
  using System.Windows.Media;

  namespace BasicWPFDataBinding
  {
      public class MyBindingColor : INotifyPropertyChanged
      {
          public event PropertyChangedEventHandler PropertyChanged;

         public MyBindingColor()
         {
             _ColorString = "Red";
             _ColorObject = Colors.Red;
         }

         // 字符串类型的ColorString
         private string _ColorString;
         public string ColorString
         {
             set
             {
                 _ColorString = value;
                 if (PropertyChanged != null)
                 {
                     PropertyChanged(this,new PropertyChangedEventArgs("ColorString"));
                 }
             }
             get
             {
                 return _ColorString;
             }
         }

         // Color对象类型的ColorObject
         private Color _ColorObject;
         public Color ColorObject
         {
             set
             {
                 _ColorObject = value;
                 if (PropertyChanged != null)
                 {
                     PropertyChanged(this, new PropertyChangedEventArgs("ColorObject"));
                 }
             }
             get
             {
                 return _ColorObject;
             }
         }
     }
 }

二、系统自带的类型转换

系统定义了一系列的常用的绑定的数据类型转换,例如可以由字符串(String)型和SolidColorBrush对象间相互转换。字符串与SolidColorBrush的转换规则有两种,一种是对应命名的颜色,如Red、Green、Blue等,另一种是#AARRGGBB或#RRGGBB组成的RGB颜色或ARGB颜色。

例如,下面的示例将字符串类型的ColorString属性绑定到Rectangle的Brush类型的Fill属性上(SolidColorBrush是Brush的子类,可以自动转换)。

XAML代码如下:

  <StackPanel Grid.Row="0" Grid.Column="0" x:Name="panelBindingColorString">
      <StackPanel.Resources>
          <c:MyBindingColor x:Key="myDataSourceA" />
      </StackPanel.Resources>
      <StackPanel.DataContext>
          <Binding Source="{StaticResource myDataSourceA}" />
      </StackPanel.DataContext>
      <TextBlock Margin="5" Text="Binding Fill To String" />
      <Rectangle Margin="5" Height="30" 
                Fill="{Binding Path=ColorString}" />
     <TextBox Margin="5" x:Name="txtValueA" />
     <Button Margin="5" Content="Change Fill" 
             x:Name="btn_ChangeColorString" Click="btn_ChangeColorString_Click" />
     <Button Margin="5" Content="Get Fill" 
             x:Name="btn_GetColorString" Click="btn_GetColorString_Click" />
 </StackPanel>

CS代码如下:

  #region 利用系统自带的字符串向SolidColorBrush对象的转换实现绑定
  private void btn_ChangeColorString_Click(object sender, RoutedEventArgs e)
  {
      try
      {
          MyBindingColor source = (MyBindingColor)(panelBindingColorString.DataContext);
          source.ColorString = txtValueA.Text;
      }
      catch (Exception ex)
     {
         MessageBox.Show(
             ex.Message,
             "System Information",
             MessageBoxButton.OK,
             MessageBoxImage.Error);
     }

 }

 private void btn_GetColorString_Click(object sender, RoutedEventArgs e)
 {
     MyBindingColor source = (MyBindingColor)(panelBindingColorString.DataContext);

     MessageBox.Show(
         string.Format("The Binding ColorString to {0}.", source.ColorString),
         "System Information",
         MessageBoxButton.OK,
         MessageBoxImage.Information);
 }
 #endregion

三、自定义的绑定类型转换

如定义自定义的绑定类型转换,需要定义一个类,对于这个类要求:

实现System.Windows.Data命名空间的IValueConverter接口,这个接口有两个抽象方法,对应两个方向的转换 为此类添加System.Windows.Data命名空间的ValueConversion这个Attribute,指定转换的源类型和目标类型 代码如下:

  using System;
  using System.Windows.Data;
  using System.Windows.Media;

  namespace BasicWPFDataBinding
  {
      /// <summary>
      /// 自定义的由Color对象向SolidColorBrush对象进行转换
      /// </summary>
     [ValueConversion(typeof(Color),typeof(SolidColorBrush))]
     public class MyColorConverter : IValueConverter
     {
         #region IValueConverter Members

         // 由Color对象向SolidColorBrush对象转换
         public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
         {
             Color val = (Color) (value);
             return new SolidColorBrush(val);
         }

         // 由SolidColorBrush对象向Color对象转换
         public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
         {
             SolidColorBrush brush = (SolidColorBrush) value;
             return brush.Color;
         }

         #endregion
     }
 }

在绑定时,需要在绑定中指明转换的类型Converter 示例代码如下:

XAML代码:

  <StackPanel Grid.Row="1" Grid.Column="0" x:Name="panelBindingColorObject">
      <StackPanel.Resources>
          <c:MyBindingColor x:Key="myDataSourceB" />
          <c:MyColorConverter x:Key="myConverter" />
      </StackPanel.Resources>
      <StackPanel.DataContext>
          <Binding Source="{StaticResource myDataSourceB}" />
      </StackPanel.DataContext>
      <TextBlock Margin="5" Text="Binding Fill To Color" />
     <Rectangle Margin="5" Height="30" 
                Fill="{Binding Path=ColorObject, Converter={StaticResource myConverter}}" />
     <TextBox Margin="5" x:Name="txtValueB" />
     <Button Margin="5" Content="Change Fill" 
             x:Name="btn_ChangeColorObject" Click="btn_ChangeColorObject_Click" />
     <Button Margin="5" Content="Get Fill" 
             x:Name="btn_GetColorObject" Click="btn_GetColorObject_Click" />
 </StackPanel>

CS代码:

  #region 使用自定义的由Color对象向SolidColorBrush对象进行转换
  private void btn_ChangeColorObject_Click(object sender, RoutedEventArgs e)
  {
      try
      {
          MyBindingColor source = (MyBindingColor)(panelBindingColorObject.DataContext);

          string txt = txtValueB.Text;
          string[] items = txt.Split(',');

         Color clr = Colors.Black;
         if (items.Length == 3)
         {
             byte r = byte.Parse(items[0], NumberStyles.HexNumber);
             byte g = byte.Parse(items[1], NumberStyles.HexNumber);
             byte b = byte.Parse(items[2], NumberStyles.HexNumber);

             clr = Color.FromRgb(r, g, b);
         }
         else if (items.Length == 4)
         {
             byte a = byte.Parse(items[0], NumberStyles.HexNumber);
             byte r = byte.Parse(items[1], NumberStyles.HexNumber);
             byte g = byte.Parse(items[2], NumberStyles.HexNumber);
             byte b = byte.Parse(items[3], NumberStyles.HexNumber);

             clr = Color.FromArgb(a, r, g, b);
         }

         source.ColorObject = clr;
     }
     catch (Exception ex)
     {
         MessageBox.Show(
             ex.Message,
             "System Information",
             MessageBoxButton.OK,
             MessageBoxImage.Error);
     }
 }

 private void btn_GetColorObject_Click(object sender, RoutedEventArgs e)
 {
     MyBindingColor source = (MyBindingColor)(panelBindingColorObject.DataContext);
     Color clr = source.ColorObject;

     string txt = string.Format(
         "Color ARGB Value is ({  X},{  X},{  X},{  X}).",
         clr.A, clr.R, clr.G, clr.B);

     MessageBox.Show(
         txt,
         "System Information",
         MessageBoxButton.OK,
         MessageBoxImage.Information);
 }
 #endregion

End at 2013/8/3 16:52:15 常伟华,感谢您

知识共享许可协议
《WPF and Silverlight 学习笔记3》 常伟华 创作。
本作品采用知识共享署名-相同方式共享 4.0 国际许可协议 | 3.0 中国大陆许可协议进行许可。

站内公告

A PHP Error was encountered

Severity: Core Warning

Message: PHP Startup: zip: Unable to initialize module Module compiled with module API=20060613 PHP compiled with module API=20090626 These options need to match

Filename: Unknown

Line Number: 0

Backtrace: