WPF里的DependencyProperty(3)

 

 

这一篇Post中,我准备先介绍WPF中DependencyProperty的一些基本用法。

在程序中使用DependencyProperty

好的,我相信你已经决定了要使用DependencyProperty而不是传统的CLR属性,正如上一篇Post所说,很多地方都需要使用到DependencyProperty,作为例子,我决定定义一个MyBorderEx,在WPF常用的"Border"控件中创建一个名为Transparency的属性,来指示它的透明度,这个属性值在0-255间变化,255表示全透明,0表示完全不透明。
我们首先定义这个DependencyProperty:

public   readonly   static  DependencyProperty TransparencyDependency  =
    DependencyProperty.Register(
        
" Transparency " ,
        
typeof (Double),
        
typeof (MyBorderEx)
        );
public  Double Transparency
{
    
get return (Double)GetValue(TransparencyDependency); }
    
set { SetValue(TransparencyDependency, value); }
}
 

这是标准的定义方式,首先定义了一个静态的DependencyProperty变量。然后封装了Transparency属性,这样可以完成向前兼容,MSDN中称为"CLR Wapper"。
我们只是定义了这个属性,不过这个属性更改时还不会自动的去更新MyBorderEx的透明度,其实WPF中包含有一个默认的名为Opacity的属性来完成这个工作,我们就借用他来实现这个功能。也许你会想这么做。

public  Double Transparency
{
    
get return (Double)GetValue(TransparencyDependency); }
    
set
    
{
        SetValue(TransparencyDependency, value);
        Opacity 
= 1 - ((double)value / 255);
    }

}
 

如果这是传统的属性,也许我们只有这么做了,但是如果你使用这种做法的话,只有在手动改变"Transparency"的值也就是调用了Set方法时才会更新Border的Opacity属性,但是如果在XAML代码中改变Transparency属性,WPF是不会调用Set方法的,因此这时类似下面的代码会没有效果。

< clr:MyBorderEx  x:Name ="pnl"  Transparency ="50" >< clr:MyBorderEx >

实际上,当你使用类似 Transparency="50" 这样的语法时,WPF会直接通过"Transparency"字符串,直接寻找到这个DependencyProperty的值(还记得Register的时候我们传了一个"Transparency"过去吗,值就是根据这个字符串找到的),然后更改这个DependencyProperty的值。

一种解决方式如下,DependecyProperty提供了属性改变通知的功能,我们可以利用DependencyProperty的这个功能,定义一个transparencyPropertyChangedCallback函数,在属性值变化(注意,这里指DependencyProperty的值发生变化而不是我们定义的"CLR wapper"的Transparency属性)时修改Opacity属性。我们现在先这样做,后面的Post中我会使用更漂亮(正统)的方式解决这个问题,完整代码如下:

class  MyBorderEx:Border
{
    
public MyBorderEx()
    
{
        
//初始化时默认给定一个背景色
        Background = Brushes.Blue;
    }

    
public readonly static DependencyProperty TransparencyDependency =
        DependencyProperty.Register(
            
"Transparency",
            
typeof(Double),
            
typeof(MyBorderEx),
            
new PropertyMetadata(new PropertyChangedCallback(transparencyPropertyChangedCallback))
            ); 

   
static void transparencyPropertyChangedCallback(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    
{
        MyBorderEx border 
= (sender as MyBorderEx);
        
if (border != null)
        
{
            border.Opacity 
=1 - Convert.ToDouble(e.NewValue) / 255;
        }

    }
 

    
public Double Transparency
    
{
        
get return (Double)GetValue(TransparencyDependency); }
        
set { SetValue(TransparencyDependency, value); }
    }

}
 

主窗体中的XAML调用代码:

< clr:MyBorderEx  x:Name ="bord"  Transparency ="50" ></ clr:MyBorderEx >  

很有意思的是,XAML中改变属性的值,并又不是通过Transparency属性的set方法,你可以在Transparency的set方法中设置断点或写一些其他代码,这些代码并不会被执行。但是,这里你并不能删除看似没什么用处的Transparency属性,也就是所谓的"CLR Wapper",甚至你不能删除这个不会被执行到的set方法。否则XAML调用中会报错。

为了证明以上结论,你可以试着把set方法中的那行SetValue(TransparencyDependency, value); 去掉,程序依然正常运行。但是如果你移除了整条set方法,编译会不通过。

好,我们把代码还原,这样已经就完成了一个DependencyProperty的定义,下面我们给这个小程序加上两个功能。

前面提到过,使用了DependencyProperty的属性可以用作数据绑定目标,我们在Window上放一个Slider测试一下数据绑定效果。目标:使用Slider动态改变Border的透明度,我全部使用XAML实现,只有很简单的两行代码

< clr:MyBorderEx  x:Name ="bord"  Transparency =" {Binding ElementName=slider1, Path=Value} " ></ clr:MyBorderEx >
< Slider  Grid.Row ="1"  HorizontalAlignment ="Left"   Name ="slider1"  Width ="130"  Value ="50"  Minimum ="0"  Maximum ="255"   />  

简单地说,把bord的Transparency绑定在slider1的Value属性上了。跑一下程序,你会发现拖动Slider,bord的背景透明度会跟着变化。

前面还提到过,使用了DependencyProperty的属性可以应用动画。我来做一个简单的Fade in和Fade out的功能,我也全部使用XAML实现:

< Button  Margin ="137,0,88,7"  Name ="btnFadeIn"  Grid.Row ="1"  Content ="FadeIn" >
    
< Button.Triggers >
        
< EventTrigger  RoutedEvent ="Button.Click" >
            
< BeginStoryboard >
                
< Storyboard  x:Name ="storyboardFadeIn"   >
                    
< DoubleAnimation  From ="255"  To ="0"  Duration ="00:00:03"  Storyboard.TargetName ="bord"  Storyboard.TargetProperty ="Transparency" />
                
</ Storyboard >
            
</ BeginStoryboard >
        
</ EventTrigger >
    
</ Button.Triggers >
</ Button >
< Button  Name ="btnFadeOut"   Margin ="0,0,28,7"  HorizontalAlignment ="Right"  Grid.Row ="1"  Width ="53"  Content ="FadeOut" >
    
< Button.Triggers >
        
< EventTrigger  RoutedEvent ="Button.Click" >
            
< BeginStoryboard >
                
< Storyboard  x:Name ="storyboardFadeOut"   >
                    
< DoubleAnimation  From ="0"  To ="255"  Duration ="00:00:03"  Storyboard.TargetName ="bord"  Storyboard.TargetProperty ="Transparency" />
                
</ Storyboard >
            
</ BeginStoryboard >
        
</ EventTrigger >
    
</ Button.Triggers >
</ Button >  

DependencyProperty基本的用法就是这样了,我们已经定义了一个简单的DependencyProperty,并且对他进行了"CLR Wapper",我们还成功的实现了在这个属性上应用数据绑定和动画,当然这只是DependencyProperty的众多功能之一。今天就介绍到这里,下面我们来思考几个问题。

  1. 我只定义了一个静态的DependencyProperty类,在实例中并没有提供一个成员变量或是什么地方存储这个属性的值,那么这个值存储到什么地方了呢?
  2. 上一篇Post提到的“反射”问题。不论在数据绑定还是动画中我们都只提供了一个属性名字符串,难道WPF真的需要使用反射读取属性数据?
  3. 如果我们在动画进行时同时通过Slider来改变DependencyProperty的值,会发生什么?此时Property中到底存储了来自哪里的值,是原始我们赋给的值?是数据绑定的值?还是当前动画的值?此时我们通过GetValue方法,会得到什么样的值呢?大家可以先用上面的程序测试一下。

小结

    今天终于开始写代码了,我们使用DependencyProperty扩展了Border控件(当然这个扩展似乎没什么意义,仅仅作为例子),并且在我们自己的DependencyProperty上实现了简单的数据绑定和动画功能。在最后,我提出了一些问题,下一篇Post中我将以这些问题为线索,探讨一下DependencyProperty中值得存储方式。
    附件里有一些主要的代码。
    另外预告一下,前段时间我花了些时间研究了下DependencyProperty,本来我准备两三篇文章就“汇报”一下研究成果的,不过一写起来却发现内容真的挺多。今后的几篇Post大致会有以下内容:
    关于DependencyProperty值的存储方式
    关于AttachedProperty
    使用PropertyMetadata的特性
    关于PropertyMetadata特性实现机制

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值