TemplateBinding与Binding区别,以及WPF自定义控件开发的遭遇
在上一次的文章WPF OnApplyTemplate 不執(zhí)行 或者執(zhí)行滯后的疑惑談到怎么正確的開發(fā)自定義控件,我們控件的樣式中,屬性的綁定一般都是用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來完成的,也就是我們在使用控件和開發(fā)自定義控件時,都能夠做到數(shù)據(jù)的展示和數(shù)據(jù)的行為分開,使用數(shù)據(jù)驅(qū)動UI的思想,對于較復(fù)雜行為的控件,我們也可以在OnApplyTemplate方法中通過GetTemplateChild方法來獲取到,當(dāng)然,這個方法的執(zhí)行時機(jī)是必須在布局過程中,如果在這之前就使用了內(nèi)部的控件,那么必然會報(bào)Null錯誤。
所以一般的樣式開發(fā)中,都是用TemplateBinding來完成,說說今天的遭遇。我就是開發(fā)一個分頁控件,點(diǎn)擊上一頁,下一頁的時候,當(dāng)前的頁碼要能夠跟著變化。顯示這個頁碼的控件那就是TextBlock,TemplateBinding了PageIndex依賴屬性。控件的后臺代碼中,對上一頁下一頁的事件,就是修改PageIndex的值。運(yùn)行起來,頁碼不會跟著變化!好,修改成Binding方式,如下:
這樣能夠正常工作了。但是WPF自家的控件用的都是TemplateBinding,都沒這問題,不甘心,繼續(xù)網(wǎng)上找資料,發(fā)現(xiàn)一篇說是自定義的依賴屬性使用TemplateBinding就是有問題的,這種bug微軟怎么能不發(fā)現(xiàn)呢,并且這都.Net4.5了,內(nèi)心感覺一定不是這樣的,終于啊,找到問題所在了,并且是在一篇排版雜亂無章的小博客中找到的。
TemplateBinding作為一種性能優(yōu)化后的Binding使用,據(jù)說是Binding比較耗資源,這個沒有求證過,但是我的程序中那么多Binding,運(yùn)行起來也不覺得慢啊,或者說是用在模板中的一種Binding優(yōu)化方式。既然是優(yōu)化過的,那么它就會少一些東西,其中一個是數(shù)據(jù)流動的方向。TemplateBinding是單方向的,即數(shù)據(jù)源到目標(biāo)的方向。這也解釋了TreeViewItem官方的樣式中,那個三角形的小箭頭,它對于是否展開(IsExpanded屬性)的屬性綁定用的就不是TempalteBinding,因?yàn)樗荒芊催^去更新數(shù)據(jù)源啊。
<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>但是在分頁控件的這個頁碼屬性上,是不需要反方向更新數(shù)據(jù)源這個功能的。所以問題也不是這兒,但必須注意這一點(diǎn),開發(fā)自定義控件的時候非常重要。
另外一個區(qū)別就是Converter,WPF中的Binding都是能夠通過Converter來轉(zhuǎn)換數(shù)據(jù)的,所以不管是TemplateBinding還是Binding都是夠使用Converter來設(shè)置轉(zhuǎn)換器,區(qū)別在于沒有設(shè)置轉(zhuǎn)換器的情況下,例如將int類型的數(shù)據(jù)綁定到TextBox的Text屬性上,Binding會將值轉(zhuǎn)換成字符串來顯示,然而TemplateBinding就不會,這就是頁碼不能顯示,也不會變化的原因。我立馬弄了一個字符串類型頁碼依賴屬性,TemplateBinding到這個Text屬性上,可以工作了。但不會這么傻,再寫一個轉(zhuǎn)換器給TemplateBinding,這也是能夠工作的。所以當(dāng)數(shù)據(jù)源的類型和目標(biāo)的類型不一致時,TemplateBinding需要自己寫轉(zhuǎn)換器來完成。
總結(jié)一下TemplateBinding與Binding區(qū)別
(1)TemplateBinding只是單方向的數(shù)據(jù)綁定
(2)TemplateBinding不會自動轉(zhuǎn)換數(shù)據(jù)類型
轉(zhuǎn)載于:https://www.cnblogs.com/HelloMyWorld/p/6744894.html
總結(jié)
以上是生活随笔為你收集整理的TemplateBinding与Binding区别,以及WPF自定义控件开发的遭遇的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 用Python和Pygame写游戏-从入
- 下一篇: linux 下清空回收站命令