在上一次的文章WPF OnApplyTemplate 不执行 或者执行滞后的疑惑谈到怎么正确的开发自定义控件,我们控件的样式中,属性的绑定一般都是用TemplateBinding来完成,如下一个基本的按钮样式:

<Style x:Key="SimpleButton" TargetType="{x:Type Button}" BasedOn="{x:Null}">
<Setter Property="FocusVisualStyle" Value="{DynamicResource SimpleButtonFocusVisual}"/>
<Setter Property="Background" Value="{DynamicResource NormalBrush}"/>
<Setter Property="BorderBrush" Value="{DynamicResource NormalBorderBrush}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}"> <!-- We use Grid as a root because it is easy to add more elements to customize the button -->
<Grid x:Name="Grid">
<Border x:Name="Border" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}"/> <!-- Content Presenter is where the text content etc is placed by the control -->
<!-- The bindings are useful so that the control can be parameterized without editing the template -->
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" RecognizesAccessKey="True"/>
</Grid> <!--Each state sets a brush on the Border in the template -->
<ControlTemplate.Triggers>
<Trigger Property="IsKeyboardFocused" Value="true">
<Setter Property="BorderBrush" Value="{DynamicResource DefaultedBorderBrush}" TargetName="Border"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Background" Value="{DynamicResource MouseOverBrush}" TargetName="Border"/>
</Trigger>
<Trigger Property="IsPressed" Value="true">
<Setter Property="Background" Value="{DynamicResource PressedBrush}" TargetName="Border"/>
<Setter Property="BorderBrush" Value="{DynamicResource PressedBorderBrush}" TargetName="Border"/>
</Trigger>
<Trigger Property="IsEnabled" Value="true"/>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Background" Value="{DynamicResource DisabledBackgroundBrush}" TargetName="Border"/>
<Setter Property="BorderBrush" Value="{DynamicResource DisabledBorderBrush}" TargetName="Border"/>
<Setter Property="Foreground" Value="{DynamicResource DisabledForegroundBrush}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

我们看到,许多属性都是用TemplateBinding来完成的,也就是我们在使用控件和开发自定义控件时,都能够做到数据的展示数据的行为分开,使用数据驱动UI的思想,对于较复杂行为的控件,我们也可以在OnApplyTemplate方法中通过GetTemplateChild方法来获取到,当然,这个方法的执行时机是必须在布局过程中,如果在这之前就使用了内部的控件,那么必然会报Null错误。

所以一般的样式开发中,都是用TemplateBinding来完成,说说今天的遭遇。我就是开发一个分页控件,点击上一页,下一页的时候,当前的页码要能够跟着变化。显示这个页码的控件那就是TextBlock,TemplateBinding了PageIndex依赖属性。控件的后台代码中,对上一页下一页的事件,就是修改PageIndex的值。运行起来,页码不会跟着变化!好,修改成Binding方式,如下:

<TextBlock Text="{Binding RelativeSource={RelativeSource TemplatedParent},Path=PageIndex}"></TextBlock>

这样能够正常工作了。但是WPF自家的控件用的都是TemplateBinding,都没这问题,不甘心,继续网上找资料,发现一篇说是自定义的依赖属性使用TemplateBinding就是有问题的,这种bug微软怎么能不发现呢,并且这都.Net4.5了,内心感觉一定不是这样的,终于啊,找到问题所在了,并且是在一篇排版杂乱无章的小博客中找到的。

TemplateBinding作为一种性能优化后的Binding使用,据说是Binding比较耗资源,这个没有求证过,但是我的程序中那么多Binding,运行起来也不觉得慢啊,或者说是用在模板中的一种Binding优化方式。既然是优化过的,那么它就会少一些东西,其中一个是数据流动的方向。TemplateBinding是单方向的,即数据源到目标的方向。这也解释了TreeViewItem官方的样式中,那个三角形的小箭头,它对于是否展开(IsExpanded属性)的属性绑定用的就不是TempalteBinding,因为他不能反过去更新数据源啊。

<Style x:Key="SimpleTreeViewItem" d:IsControlPart="True" TargetType="{x:Type TreeViewItem}">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="HorizontalContentAlignment" Value="{Binding Path=HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
<Setter Property="VerticalContentAlignment" Value="{Binding Path=VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
<Setter Property="Padding" Value="1,0,0,0"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TreeViewItem}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition MinWidth="19" Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<!--注意这里-->
<ToggleButton x:Name="Expander" Style="{DynamicResource SimpleTreeViewItemToggleButton}" IsChecked="{Binding Path=IsExpanded, RelativeSource={RelativeSource TemplatedParent}}" ClickMode="Press"/>
<Border Grid.Column="1" x:Name="Selection_Border" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}">
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" x:Name="PART_Header" ContentSource="Header"/>
</Border>
<ItemsPresenter Grid.Column="1" Grid.ColumnSpan="2" Grid.Row="1" x:Name="ItemsHost"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsExpanded" Value="false">
<Setter Property="Visibility" Value="Collapsed" TargetName="ItemsHost"/>
</Trigger>
<Trigger Property="HasItems" Value="false">
<Setter Property="Visibility" Value="Hidden" TargetName="Expander"/>
</Trigger>
<Trigger Property="IsSelected" Value="true">
<Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}" TargetName="Selection_Border"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.HighlightTextBrushKey}}"/>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsSelected" Value="true"/>
<Condition Property="IsSelectionActive" Value="false"/>
</MultiTrigger.Conditions>
<Setter Property="Background" Value="red" TargetName="Selection_Border"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
</MultiTrigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

但是在分页控件的这个页码属性上,是不需要反方向更新数据源这个功能的。所以问题也不是这儿,但必须注意这一点,开发自定义控件的时候非常重要。

另外一个区别就是Converter,WPF中的Binding都是能够通过Converter来转换数据的,所以不管是TemplateBinding还是Binding都是够使用Converter来设置转换器,区别在于没有设置转换器的情况下,例如将int类型的数据绑定到TextBox的Text属性上,Binding会将值转换成字符串来显示,然而TemplateBinding就不会,这就是页码不能显示,也不会变化的原因。我立马弄了一个字符串类型页码依赖属性,TemplateBinding到这个Text属性上,可以工作了。但不会这么傻,再写一个转换器给TemplateBinding,这也是能够工作的。所以当数据源的类型和目标的类型不一致时,TemplateBinding需要自己写转换器来完成

总结一下TemplateBinding与Binding区别

(1)TemplateBinding只是单方向的数据绑定
(2)TemplateBinding不会自动转换数据类型

TemplateBinding与Binding区别,以及WPF自定义控件开发的遭遇的更多相关文章

  1. WPF自定义控件开发实例 - ColorPicker

    开发环境:Win10 + VS2017 + .Net4.5 这个 ColorPicker 是<<WPF编程宝典:使用C#2012和.NET4.5>>这本书中的例子.这里我记录一 ...

  2. WPF自定义控件与样式(3)-TextBox & RichTextBox & PasswordBox样式、水印、Label标签、功能扩展

    一.前言.预览 申明:WPF自定义控件与样式是一个系列文章,前后是有些关联的,但大多是按照由简到繁的顺序逐步发布的等,若有不明白的地方可以参考本系列前面的文章,文末附有部分文章链接. 本文主要是对文本 ...

  3. WPF自定义控件与样式(15)-终结篇 & 系列文章索引 & 源码共享

    系列文章目录  WPF自定义控件与样式(1)-矢量字体图标(iconfont) WPF自定义控件与样式(2)-自定义按钮FButton WPF自定义控件与样式(3)-TextBox & Ric ...

  4. WPF自定义控件与样式(12)-缩略图ThumbnailImage /gif动画图/图片列表

    一.前言 申明:WPF自定义控件与样式是一个系列文章,前后是有些关联的,但大多是按照由简到繁的顺序逐步发布的等,若有不明白的地方可以参考本系列前面的文章,文末附有部分文章链接. 本文主要针对WPF项目 ...

  5. WPF自定义控件与样式(15)-终结篇

    原文:WPF自定义控件与样式(15)-终结篇 系列文章目录  WPF自定义控件与样式(1)-矢量字体图标(iconfont) WPF自定义控件与样式(2)-自定义按钮FButton WPF自定义控件与 ...

  6. WPF自定义控件创建

    WPF自定义控件创建 本文简单的介绍一下WPF自定义控件的开发. 首先,我们打开VisualStudio创建一个WPF自定义控件库,如下图: 然后,我们可以看到创建的解决方案如下: 在解决方案中,我们 ...

  7. WPF 如何创建自己的WPF自定义控件库

    在我们平时的项目中,我们经常需要一套自己的自定义控件库,这个特别是在Prism这种框架下面进行开发的时候,每个人都使用一套统一的控件,这样才不会每个人由于界面不统一而造成的整个软件系统千差万别,所以我 ...

  8. 【转】WPF自定义控件与样式(12)-缩略图ThumbnailImage /gif动画图/图片列表

    一.前言 申明:WPF自定义控件与样式是一个系列文章,前后是有些关联的,但大多是按照由简到繁的顺序逐步发布的等,若有不明白的地方可以参考本系列前面的文章,文末附有部分文章链接. 本文主要针对WPF项目 ...

  9. 【转】WPF自定义控件与样式(3)-TextBox & RichTextBox & PasswordBox样式、水印、Label标签、功能扩展

    一.前言.预览 申明:WPF自定义控件与样式是一个系列文章,前后是有些关联的,但大多是按照由简到繁的顺序逐步发布的等. 本文主要是对文本输入控件进行样式开发,及相关扩展功能开发,主要内容包括: 基本文 ...

随机推荐

  1. j++与++j

    (j++)+(++j)+(++j) 核心部分汇编代码 执行顺序j原始值为5,存放在栈的ebp-4中;q无初始值,存放在栈的ebp-8中先执行一次j自增赋值++j ;j=6执行第一个加法运算;值存放在C ...

  2. asp弹出层

    asp弹出层 <style type="text/css"> html, body { height: %; width: %; } .white_content { ...

  3. Installshield build all installer in development computer

    Step: Copy all "SetupPrerequisites" from build server. please make sure below items: Insta ...

  4. This account is currently not available

    今天在linux下切换用户发现提示This account is currently not available,说是无效用户了后来网上查了一下发现是用户的shell禁止登录了,解决方法只要开启she ...

  5. OpenGL — GLFW — 颜色

    OpenGL - GLFW - 颜色 参考教程:https://learnopengl-cn.readthedocs.io/zh/latest/02%20Lighting/01%20Colors/ 既 ...

  6. php学习笔记-变量的作用域

    这个东西很难理解,但很重要,我觉得非常容易出错. PHP中的变量按照作用域分为有两种,一种是global,一种是local. 函数内部声明的变量就叫local型变量,只能在函数内部被访问到.一句话,l ...

  7. 做c语言的码农专业发展方向

    写了几年C语言代码,最近在思索,何去何从比较好? 搜索了一下,发现几个答案: 2015年10月编程语言排行榜 丢开C语言在教学应用外.在目前C语言的实际应用中.常见的应用的情景有如下: 内核/驱动,b ...

  8. unity3d 刷新速率

    using UnityEngine; using UnityEngine.UI; public class Text : MonoBehaviour { public Text t; private ...

  9. C# Guid should contain 32 digits with 4 dashes (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx).

    一.问题描述 在做C# 的 Guid 转换时,出现这个问题:Guid should contain 32 digits with 4 dashes (xxxxxxxx-xxxx-xxxx-xxxx-x ...

  10. 《OD学hadoop》20160910某旅游网项目实战

    一.event事件分析 叶子节点只计算一次 父节点的触发次数由子节点的数量节点 事件流:是由业务人员定义的一系列的具有前后顺序的事件构成的用户操作行为,至少包括两个事件以上. 目标:以事件流为单位,分 ...