本文想通过由浅入深的讲解让读者比较深的理解依赖属性.  首先,我们回顾一下依赖属性的发展历史.

  最初,人们提出面向对象编程时,并没有属性这个说法,当时叫做成员变量.一个对象由成员变量和成员函数组成,如下:

Public Class A{
      Public int Index;//成员变量
    Public void Fun(){} //成员函数
}
      

后来,提出了对成员变量的改进,增加了get/set 方法,成员变量自然也叫属性了。.net采用了这种方法:

Public Class A{
      Private int index;//属性
   Public  int Index{
        Set{index = Value;}
        Get{return index ;}
     }
   Public void Fun(){} //方法
}

到了WPF, 终于变成依赖属性(WPF大部分属性都是依赖属性)。我们先看一个依赖属性的实现,再理解。

 public partial class MyButton : Button {
        public static DependencyProperty PressedImageProperty;//依赖属性

        public string PressedImage {
            get { return (string)GetValue(PressedImageProperty); }
            set { SetValue(PressedImageProperty, value); }
        }

        static MyButton() {
            PressedImageProperty = DependencyProperty.Register(
                "PressedImage", typeof(string), typeof(MyButton),
                new FrameworkPropertyMetadata(""));
        }

        public MyButton () {
            InitializeComponent();
        }

上面实现了一个PressedImage的依赖属性。和.net比较有2点明显的区别:

  1。依赖属性是静态的。

  2。多了一个Register。

 下面就要进入主题了,依赖属性的数据绑定是什么,怎么实现?

  通过例子说明。对上文MyButton进行修进,实现以下功能:当MyButton被按下时显示图片"c:\images\ButtonPressed.png",否则显示图片"c:\images\ButtonNormal.png".   xaml代码

    <Button.Template>
        <ControlTemplate TargetType="{x:Type Button}">
            <Grid>
                <Image x:Name="imgNormal" 
                       Source="c:\images\ButtonNormal.png" Visibility="Visible"/>
                <Image x:Name="imgPressed" 
                       Source="c:\images\ButtonPressed.png" Visibility="Hidden" />
            </Grid>
            <ControlTemplate.Triggers>
                <Trigger Property="IsPressed" Value="True">
                    <Setter TargetName="imgNormal" Property="Visibility" Value="Hidden"/>
                    <Setter TargetName="imgPressed" Property="Visibility" Value="Visible"/>
                </Trigger>
            </ControlTemplate.Triggers>
        </ControlTemplate>
    </Button.Template>
  

cs文件代码:

 public partial class MyButton : Button {
      
        public MyButton () {
            InitializeComponent();
        }

上面的代码看不懂也没关系,并没有用到依赖属性。 但同时,这个MyButton并没有使用价值,因为他的图片是固定的。我们希望MyButton的图片可以由用户来设定。在上面的xaml文件中,只需改变Image的Source属性。WPF的方法就是数据绑定,也就是把Image的Source属性绑定到一个变量,修改一下xaml:

<Button.Template> 
<ControlTemplate TargetType="{x:Type Button}"> 
<Grid> 
<Image x:Name="imgNormal"
 Source="{Binding Path=NormalImage}" Visibility="Visible"/>
<Image x:Name="imgPressed"
Source="{Binding Path=PressedImage}" Visibility="Hidden" /> 
</Grid>
 <ControlTemplate.Triggers>
 <Trigger Property="IsPressed" Value="True">
 <Setter TargetName="imgNormal" Property="Visibility" Value="Hidden"/> 
<Setter TargetName="imgPressed" Property="Visibility" Value="Visible"/> 
</Trigger> 
</ControlTemplate.Triggers> 
</ControlTemplate>
 </Button.Template>

imgNormal的图片路径绑定变量NormalImage, imgPressed的图片路径绑定到PressedImage,再在MyButton类中增加2个属性NormalImage,PressedImage

 public partial class MyButton : Button {
           public   string NormalImage;
           public   string NormalImage;
            public MyButton () {
            InitializeComponent();
       }

 在使用MyButton时用以下语句访问: 

<my:MyButton 
NormalImage="c:\images\ButtonNormal.png"
PressedImage="c:\images\ButtonPressed.png" />

 搞定!!怎么?编译通不过?因为NormalImage和PressedImage不是依赖属性,所以编译无法通过。

 我开始对依赖属性开始反感了,为什么一定要是依赖属性?微软的xaml如支持普通属性,那问题不就没了吗?

这个问题我们得回顾一下WPF的基本理念:真正做到了分离界面设计人员与开发人员的工作。也就是说,xaml文件呈现的是用户界面,从xaml到用户界面的转化不是由编译器直接编译成用户界面的运行代码(这样和原来的WIN FORM又没有区别了),而是由WPF本身类库来来完成的,打个比方,XAML有如下代码:

<Button Background="White"></Button>

 其中的“White”是如何与C#中的System.Windows.Media.Brushes.White等价的呢?。原来WPF提供了Brush数据类型的转换器。对于属性, WPF提供了一个叫DependencyProperty的类来支持,这个类就是依赖属性。因此,NormalImage和PressedImage必须是依赖属性也是理所当然的了。MyButton.cs的最后代码如下: 

 public partial class MyButton : Button {
        public static DependencyProperty NormalImageProperty;
        public static DependencyProperty PressedImageProperty;

        public string NormalImage {
            get { return (string)GetValue(NormalImageProperty); }
            set { SetValue(NormalImageProperty, value); }
        }

        public string PressedImage {
            get { return (string)GetValue(PressedImageProperty); }
            set { SetValue(PressedImageProperty, value); }
        }

        static MyButton () {
            NormalImageProperty = DependencyProperty.Register(
                "NormalImage", typeof(string), typeof(MyButton ),
                new FrameworkPropertyMetadata(""));

            PressedImageProperty = DependencyProperty.Register(
                "PressedImage", typeof(string), typeof(MyButton ),
                new FrameworkPropertyMetadata(""));
        }

        public MyButton () {
            InitializeComponent();
        }

因此,依赖属性是WPF为了真正做到了分离界面设计人员与开发人员的工作,对原来winform属性的一种改进,这样理解比MSDN的解释简单多了。

 从依赖属性的定义来看,还剩一个问题:Register有什么用,又是怎么实现的?下篇文章来讨论。

关于分离界面设计人员与开发人员的工作的一点思考:

 WPF对象由xaml文件和cs文件组成。习惯上,用户动作由xaml文件来关联cs文件的相关实现代码。比如鼠标的Click事件。也就是说,界面设计人员必须关心用户事件在逻辑代码中的功能入口。我在想, WPF对象为什么不能由3个文件组成,xaml文件,cs文件,再加一个xaml和cs的关联文件,该文件由开发人员来维护,这样,界面设计人员彻底的不用再关心逻辑代码了。

作者: 钟湘光 发表于 2011-05-25 13:49 原文链接

推荐.NET配套的通用数据层ORM框架:CYQ.Data 通用数据层框架