WPF控件样式和模板全解析-Label控件
可能有些小伙伴觉得Label控件比较简单,不需要再单独介绍它的样式和模板。
这里呢,我还是觉得很有必要拿出来讲一下,通过剖析它的内部模板,来了解Label控件更深层次的原理。
Label控件的默认样式和模板
依旧是使用Blend获取默认的样式和模板,如下所示:
1 <Style x:Key="LabelStyle1" TargetType="{x:Type Label}"> 2 <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/> 3 <Setter Property="Background" Value="Transparent"/> 4 <Setter Property="Padding" Value="5"/> 5 <Setter Property="HorizontalContentAlignment" Value="Left"/> 6 <Setter Property="VerticalContentAlignment" Value="Top"/> 7 <Setter Property="Template"> 8 <Setter.Value> 9 <ControlTemplate TargetType="{x:Type Label}"> 10 <Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}" SnapsToDevicePixels="true"> 11 <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/> 12 </Border> 13 <ControlTemplate.Triggers> 14 <Trigger Property="IsEnabled" Value="false"> 15 <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/> 16 </Trigger> 17 </ControlTemplate.Triggers> 18 </ControlTemplate> 19 </Setter.Value> 20 </Setter> 21 </Style>
拆解Button样式和模板
从上面的样式可以看到,Label控件的模板和Button控件几乎一样,都是Border里面套一个ContentPresenter控件
1 <Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}" SnapsToDevicePixels="true"> 2 <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/> 3 </Border>
然后就是一个触发器,用于设置禁用状态下Label控件文字的颜色
1 <ControlTemplate.Triggers> 2 <Trigger Property="IsEnabled" Value="false"> 3 <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/> 4 </Trigger> 5 </ControlTemplate.Triggers>
Label和TextBlock的区别
一直就听说TextBlock是轻量级的文本显示控件,Label更重量级。
但是根据前面文章中,关于ContentPresenter控件的介绍就可以知道:ContentPresenter控件只是一个壳,在显示文本时最终还是使用的TextBlock控件。
那为什么还是会有这个说法呢?
原因有以下几个方面:
1、继承链不同,TextBlock的继承关系更简单,Label更复杂
-
TextBlock 直接继承自
FrameworkElement。它处于WPF元素体系的最底层,属于“呈现元素”,只负责把自己画在屏幕上。 - Label 继承自
ContentControl,完整的继承关系是ContentControl->Control->FrameworkElement,而Control控件拥有更多的依赖属性及其它特性,所以它的开销就会大一点,相比之下,TextBlock的功能就更单一。
2、视觉树的层级深度
TextBlock只有一层,而Label控件会有Label->Border->ContentPresenter->TextBlock多层。
WPF的布局过程(Measure和Arrange)会递归遍历视觉树,所以每多一层嵌套,CPU就需要多做一次计算。
通常情况下,对于单个Label影响不大,但是当数量比较多时,例如大量的ListBox或DataGrid列表项时,就会对性能造成一定的影响。
3、焦点处理开销
Label默认Focusable=true,TextBlock默认Focusable=false,
而且Label可以使用助记快捷键 AccessKey功能,例如下面的代码,按按下键盘上的Alt+H时,可以快速将光标定位至TextBox元素。
1 <StackPanel> 2 <Label Content="_HelloWorld" Target="{Binding ElementName=tbox2}"></Label> 3 <TextBox x:Name="tbox2"></TextBox> 4 </StackPanel>

4、布局计算问题
-
TextBlock 的测量逻辑较专一,只计算文字的宽高。
-
Label 属于内容控件,可以所以放置任意控件,如图片、布局面板等。这就导致在计算界面布局时,它的测量算法比
TextBlock复杂。

浙公网安备 33010602011771号