WPF依赖属性的正确学习方法
前言
我在学习wpf的早期,对依赖属性理解一直都非常的不到位,其恶果就是,我每次在写依赖属性的时候,需要翻过去的代码来复制黏贴。
相信很多朋友有着和我相同的经历,所以这篇文章希望能帮助到那些刚刚开始学依赖属性的朋友。
那些[讨厌]的依赖属性的讲解文章
初学者肯定会面临一件事,就是百度,谷歌,或者msdn来查看依赖属性的定义和使用,而这些文章虽然都写的很好,但,那是相对于已经学会使用依赖属性的朋友而言。
而对于初学者而言,说是误导都不过分。
比如,官网的这篇文章
介绍依赖属性是这样。
public static readonly dependencyproperty isspinningproperty =
dependencyproperty.register(
"isspinning", typeof(boolean),
typeof(mycode)
);
public bool isspinning
{
get { return (bool)getvalue(isspinningproperty); }
set { setvalue(isspinningproperty, value); }
}
他做了一个定义,然后告诉你,依赖属性的定义格式如此。
如果你是个初学者,你想不疑惑都很难。因为没人能把这种定义给背下来。
其结果就是,你要和我当初一样,每次定义依赖属性,都要去复制黏贴。但这并不是最大的恶果,最大的恶果是,因为太过复杂的定义,让你放弃了对他理解,就记住了依赖属性要复制黏贴,从而导致了,你丧失了对依赖属性灵活运用的能力。
正确的理解依赖属性
如何正确的理解依赖属性呢?
很简单,拆分一下就可以理解了。
现在我们来拆分依赖属性,首先拆分他的定义,将依赖和属性拆分。
我们先看属性,如下,我们定义了一个属性。
private bool _isspinning;
public bool isspinning
{
get { return _isspinning; }
set { _isspinning = value; }
}
然后我们使用dependencyproperty类定义一个对象,这个对象将作为isspinning属性的依赖,如下:
public static readonly dependencyproperty isspinningproperty
然后,我们在将这个依赖对象,注册到属性isspinning的所在类上,如下:
dependencyproperty.register( "isspinning", typeof(bool), typeof(你的属性所在的类的名称));
从注册代码中,我们可以看到,他注册了三个信息:
1,当前dependencyproperty类定义的对象isspinningproperty,依赖于属性isspinning。
2,对象isspinningproperty的依赖类型与属性isspinning的类型一样都是bool。
3,对象isspinningproperty注册的类是声明属性isspinning的类,即,在其他类里,将看不到该依赖对象。
现在,我们做最后的操作,修改属性,将依赖对象isspinningproperty与属性isspinning绑定。
如何绑定呢?很简单,将我们属性定义里的【private bool _isspinning】替换为我们刚刚定义的依赖【isspinningproperty】即可。
public bool isspinning
{
get { return (bool)getvalue(isspinningproperty); }
set { setvalue(isspinningproperty, value); }
}
这里我们看到了,在给属性赋值和取值时,用到了getvalue和setvalue,他们俩是哪来的呢?
使用f12,我们跟踪进去,发现它们是类dependencyproperty里定义的方法,那么为什么我们在窗体里也可以用呢?
很简单,我们跟进一下window的父类,发现最后的父类visual继承了dependencyproperty,所以我们可以直接使用getvalue和setvalue来赋值和获取依赖对象的值。也就是只要是继承了类dependencyproperty的子类,都可以使用依赖属性。
完整版依赖属性定义代码:
public static readonly dependencyproperty isspinningproperty =
dependencyproperty.register("isspinning", typeof(bool), typeof(dependecyusercontrol));
public bool isspinning
{
get { return (bool)getvalue(isspinningproperty); }
set { setvalue(isspinningproperty, value); }
}
到这里,依赖属性的拆分就完事了,现在,大家应该很清楚依赖属性到底是什么了吧。
ps:有没有人曾经告诉你,依赖属性的命名必须是 属性名+property,然后你还信以为真了。哈哈。
依赖属性的简单应用
现在让我们来自定义一个带依赖属性的系统控件来加深记忆。
public class kbutton : button
{
public static readonly dependencyproperty foreimageproperty;
public static readonly dependencyproperty backimageproperty;
public static readonly dependencyproperty mouseoverbackcolorproperty;
public static readonly dependencyproperty stretchproperty;
static kbutton()
{
foreimageproperty = dependencyproperty.register("foreimage", typeof(string), typeof(kbutton),null);
foreimageproperty = dependencyproperty.register("backimage", typeof(string), typeof(kbutton),null);
mouseoverbackcolorproperty = dependencyproperty.register("mouseoverbackcolor", typeof(brush), typeof(kbutton), null);
stretchproperty = dependencyproperty.register("stretch", typeof(stretch), typeof(kbutton), null);
defaultstylekeyproperty.overridemetadata(typeof(kbutton), new frameworkpropertymetadata(typeof(kbutton)));//使kbutton去读取kbutton类型的样式,而不是去读取button的样式
}
public string foreimage
{
get { return (string)getvalue(foreimageproperty); }
set { setvalue(foreimageproperty, value); }
}
public string backimage
{
get { return (string)getvalue(backimageproperty); }
set { setvalue(backimageproperty, value); }
}
public brush mouseoverbackcolor
{
get { return (brush)getvalue(mouseoverbackcolorproperty); }
set { setvalue(mouseoverbackcolorproperty, value); }
}
public stretch stretch
{
get { return (stretch)getvalue(stretchproperty); }
set { setvalue(stretchproperty, value); }
}
}
如上述代码所示,我们定义了一个继承至button的类kbutton。
在kbuttion中,我们定义了四个依赖属性:
foreimageproperty:按钮的前景图片。
backimageproperty:按钮的背景图片。
mouseoverbackcolorproperty:按钮在鼠标经过时的颜色。
stretchproperty:按钮图片的拉伸模式。
代码非常简洁,除了四个依赖属性之外,什么也没有;现在我们去定义kbutton类型的样式。
为了演示方便,我直接将样式定义在了app.xaml文件内。
<style targettype="{x:type local:kbutton}">
<setter property="template">
<setter.value>
<controltemplate>
<dockpanel name="dpcon" width="{binding width, relativesource={x:static relativesource.templatedparent}}"
height="{binding height, relativesource={x:static relativesource.templatedparent}}"
background="{binding background, relativesource={x:static relativesource.templatedparent}}"
tooltip="{binding tooltip, relativesource={x:static relativesource.templatedparent}}"
>
<dockpanel dockpanel.dock="top" name="dpbtn">
<dockpanel.background>
<imagebrush imagesource="{binding foreimage, relativesource={x:static relativesource.templatedparent}}" stretch="{binding stretch,relativesource={x:static relativesource.templatedparent}}"/>
</dockpanel.background>
<textblock fontsize="15" verticalalignment="center" horizontalalignment="center" foreground="#f9fcff" text="{binding content, relativesource={x:static relativesource.templatedparent}}"></textblock>
</dockpanel>
</dockpanel>
<controltemplate.triggers>
<datatrigger binding="{binding ismouseover,relativesource={x:static relativesource.self}}" value="true">
<setter property="background" targetname="dpbtn">
<setter.value>
<imagebrush imagesource="{binding backimage, relativesource={x:static relativesource.templatedparent}}" stretch="{binding stretch,relativesource={x:static relativesource.templatedparent}}"/>
</setter.value>
</setter>
<setter property="background" targetname="dpcon" value="{binding mouseoverbackcolor, relativesource={x:static relativesource.templatedparent}}"></setter>
</datatrigger>
<datatrigger binding="{binding backimage,relativesource={x:static relativesource.self},mode=twoway}" value="{x:null}">
<setter property="background" targetname="dpbtn">
<setter.value>
<imagebrush imagesource="{binding foreimage, relativesource={x:static relativesource.templatedparent}}" stretch="{binding stretch,relativesource={x:static relativesource.templatedparent}}"/>
</setter.value>
</setter>
</datatrigger>
<trigger property="isenabled" value="true"/>
<trigger property="isenabled" value="false">
<setter property="foreground" value="gray"/>
</trigger>
</controltemplate.triggers>
</controltemplate>
</setter.value>
</setter>
</style>
样式代码如上所示,也非常简单,就是定义了一个模板,然后在模板里摆放好按钮背景图和按钮文字的位置。然后将我们之前定义好的依赖属性绑定到对应的值上。
其中需要注意的是,在模板中绑定自定义依赖属性,是使用relativesource.templatedparent的,如{binding foreimage, relativesource={x:static relativesource.templatedparent}}。
而在模板的数据事件datatrigger中,绑定依赖属性的模式却是分两种的。
第一种,绑定数据事件datatrigger的条件时,使用relativesource.self,如{binding ismouseover,relativesource={x:static relativesource.self}}。
第二种,条件成立,触发模板变化时,使用relativesource.templatedparent,如{binding backimage, relativesource={x:static relativesource.templatedparent}}。
----------------------------------------------------------------------------------------------------
现在我们使用下我们制作好的自定义控件,代码如下所示:
<dockpanel>
<stackpanel>
<local:kbutton height="50" width="50" stretch="none" foreimage="/image/关闭.png" backimage="/image/关闭退出.png" background="gray" mouseoverbackcolor="brown"/>
<local:kbutton height="50" width="50" margin="0,10,0,0" stretch="none" foreimage="/image/关闭.png" background="gray" mouseoverbackcolor="brown"/>
<local:kbutton height="100" width="100" margin="0,10,0,0" content="篮子" stretch="fill" foreimage="/image/篮子.png" background="gray" mouseoverbackcolor="brown"/>
</stackpanel>
</dockpanel>
界面效果如下:
自定义用户控件中使用依赖属性
首先我们添加新项,然后选择用户控件。
然后,我们添加一个依赖属性headertitle,同时设置当前控件的datacontext为自身—this.datacontext = this。
public string headertitle
{
get { return (string)getvalue(headertitleproperty); }
set { setvalue(headertitleproperty, value); }
}
public static readonly dependencyproperty headertitleproperty = dependencyproperty.register("headertitle", typeof(string), typeof(dependecyusercontrol), null);
public dependecyusercontrol()
{
this.datacontext = this;
initializecomponent();
}
现在,我们在用户控件的xaml页面添加一个textblock,并绑定他的text为我们刚刚定义的headertitle,代码如下所示。
<grid>
<textblock text = "{binding headertitle}" textalignment="center"></textblock>
</grid>
接着我们回到主窗体,引用这个用户控件,代码如下所示:
<local:dependecyusercontrol height = "30" headertitle="我是header" dockpanel.dock="top"></local:dependecyusercontrol>
运行结果:
可以看到,我们成功在主页面设置了用户控件的依赖属性,并让他成功的绑定到了用户控件中的textblock的text属性。也就是说,我们简单的实现了header的title动态设置。
结语
wpf拥有非常强大的自定义能力,而,正确的学会了依赖属性是体会到它强大的第一步。
----------------------------------------------------------------------------------------------------
到此wpf依赖属性的正确学习方法就已经讲解完成了。
代码已经传到github上了,欢迎大家下载。
github地址:https://github.com/kiba518/wpfdependency
----------------------------------------------------------------------------------------------------
注:此文章为原创,欢迎转载,请在文章页面明显位置给出此文链接!
若您觉得这篇文章还不错,请点击下方的【推荐】,非常感谢!
推荐阅读
-
WPF TextBox绑定Int类型的属性
-
CSS box-sizing属性的正确用法
-
img标签中alt和title属性的正确使用
-
WPF Binding中的RelativeSource属性
-
wpf 的依赖属性只能在loaded 事件之后才能取到
-
SQLServer属性集的闭包,最小函数依赖集等题目讲解
-
WPF xaml中列表依赖属性的定义
-
未能加载文件或程序集“**********”或它的某一个依赖项。试图加载格式不正确的程序。
-
未能加载文件或程序集“Oracle.DataAccess”或它的某一个依赖项.试图加载格式不正确的程序
-
开发函数计算的正确姿势——使用交互模式安装依赖 python