[UWP]了解模板化控件(3):实现HeaderedContentControl

简介: 原文:[UWP]了解模板化控件(3):实现HeaderedContentControl1. 概述 来看看这段XMAL: 是不是觉得它们中出了一个叛徒?这个示例中除了ListBox控件其它都自带Header,但是ListBox没有Header属性,只好用一个TextBlock模仿它的Header。
原文: [UWP]了解模板化控件(3):实现HeaderedContentControl

1. 概述

来看看这段XMAL:

<StackPanel Width="300">
    <TextBox Header="TextBox" />
    <ComboBox Header="ComboBox" HorizontalAlignment="Stretch"/>
    <AutoSuggestBox Header="AutoSuggestBox" />
    <TextBlock Text="ListBox" />
    <ListBox>
        <ListBoxItem Content="ListBoxItem 1" />
        <ListBoxItem Content="ListBoxItem 2" />
        <ListBoxItem Content="ListBoxItem 3" />
    </ListBox>
</StackPanel>

img_d69fe20f518c761bc3dcc17fec58b821.png

是不是觉得它们中出了一个叛徒?这个示例中除了ListBox控件其它都自带Header,但是ListBox没有Header属性,只好用一个TextBlock模仿它的Header。这样就带来一个问题:只有ListBox的Header高度和其它控件不一致。

既然现在讨论的是自定义控件,这里就用自定义控件的方式解决这个问题。首先想到最简单的方法,就是自定义一个HeaderedContentControl,如名字所示,这个控件继承自ContentControl并拥有Header属性,用起来大概是这样:

<HeaderedContentControl Header="ListBox">
    <ListBox/>
</HeaderedContentControl>

这样,只要在HeaderedContentControl的样式中模仿其它含Header属性的控件,就能统一Header的外观。

WPF中本来就有这个控件,它是Expander、GroupBox、TabItem等诸多拥有Header属性的控件的基类,十分方便好用。UWP中模仿这个控件很简单,而且很适合用来学习自定义控件的进阶知识。

2. 定义HeaderedContentControl结构

比起WPF,借鉴Silverlight的HeaderedContentControl比较好,因为Silverlight的比较简单。HeaderedContentControl只需要在继承ContentControl后添加两个属性:Header和HeaderTemplate。

public class HeaderedContentControl : ContentControl
{
    public HeaderedContentControl()
    {
        this.DefaultStyleKey = typeof(HeaderedContentControl);
    }

    /// <summary>
    /// 获取或设置Header的值
    /// </summary>  
    public object Header
    {
        get { return (object)GetValue(HeaderProperty); }
        set { SetValue(HeaderProperty, value); }
    }

    /// <summary>
    /// 标识 Header 依赖属性。
    /// </summary>
    public static readonly DependencyProperty HeaderProperty =
        DependencyProperty.Register("Header", typeof(object), typeof(HeaderedContentControl), new PropertyMetadata(null, OnHeaderChanged));

    private static void OnHeaderChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
    {
        HeaderedContentControl target = obj as HeaderedContentControl;
        object oldValue = (object)args.OldValue;
        object newValue = (object)args.NewValue;
        if (oldValue != newValue)
            target.OnHeaderChanged(oldValue, newValue);
    }

    /// <summary>
    /// 获取或设置HeaderTemplate的值
    /// </summary>  
    public DataTemplate HeaderTemplate
    {
        get { return (DataTemplate)GetValue(HeaderTemplateProperty); }
        set { SetValue(HeaderTemplateProperty, value); }
    }

    /// <summary>
    /// 标识 HeaderTemplate 依赖属性。
    /// </summary>
    public static readonly DependencyProperty HeaderTemplateProperty =
        DependencyProperty.Register("HeaderTemplate", typeof(DataTemplate), typeof(HeaderedContentControl), new PropertyMetadata(null, OnHeaderTemplateChanged));

    private static void OnHeaderTemplateChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
    {
        HeaderedContentControl target = obj as HeaderedContentControl;
        DataTemplate oldValue = (DataTemplate)args.OldValue;
        DataTemplate newValue = (DataTemplate)args.NewValue;
        if (oldValue != newValue)
            target.OnHeaderTemplateChanged(oldValue, newValue);
    }

    protected virtual void OnHeaderChanged(object oldValue, object newValue)
    {
    }

    protected virtual void OnHeaderTemplateChanged(DataTemplate oldValue, DataTemplate newValue)
    {
    }
}

3. 设计样式

在C:\Program Files (x86)\Windows Kits\10\DesignTime\CommonConfiguration\Neutral\UAP\10.0.14393.0\Generic\generic.xaml中找到ContentControl的样式。

img_7b78649c5b041eabf2fbf55d60d0b6c2.png

再从TextBox的Style中找到HeaderContentPresenter
img_c78c743352ac37f98de87d5443b326b8.png

提示: 随便找个有ThemeResource的XAML,譬如Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}",在资源名称(ApplicationPageBackgroundThemeBrush)上按"F12",即可导航到存放ThemeResource的generic.xaml。

组合起来,HeaderedContentControl的默认样式就完成了。

<Style TargetType="local:HeaderedContentControl">
    <Setter Property="HorizontalContentAlignment"
            Value="Left" />
    <Setter Property="VerticalContentAlignment"
            Value="Top" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="local:HeaderedContentControl">
                <StackPanel>
                    <ContentPresenter x:Name="HeaderContentPresenter"
                                      Foreground="{ThemeResource TextControlHeaderForeground}"
                                      Margin="0,0,0,8"
                                      Content="{TemplateBinding Header}"
                                      ContentTemplate="{TemplateBinding HeaderTemplate}"
                                      FontWeight="Normal" />
                    <ContentPresenter Content="{TemplateBinding Content}"
                                      ContentTemplate="{TemplateBinding ContentTemplate}"
                                      Margin="{TemplateBinding Padding}"
                                      ContentTransitions="{TemplateBinding ContentTransitions}"
                                      HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                      VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
                </StackPanel>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

4. 使用

 <StackPanel Visibility="Collapsed">
    <TextBox Header="TextBox" />
    <ComboBox Header="ComboBox"
              HorizontalAlignment="Stretch" />
    <AutoSuggestBox Header="AutoSuggestBox" />
    <local:HeaderedContentControl Header="ListBox">
        <ListBox>
            <ListBoxItem Content="ListBoxItem 1" />
            <ListBoxItem Content="ListBoxItem 2" />
            <ListBoxItem Content="ListBoxItem 3" />
        </ListBox>
    </local:HeaderedContentControl>
</StackPanel>

img_93e13a22b0588d7f11f17d8d333edb6c.png

调用代码及效果。这样外观就统一了。

注意: 我移除了 x:DeferLoadStrategy="Lazy"这句,这个知识点比较适合放在有关性能的主题里讨论。

目录
相关文章
|
C# 开发工具 Windows
WindowsXamlHost:在 WPF 中使用 UWP 控件库中的控件
原文 WindowsXamlHost:在 WPF 中使用 UWP 控件库中的控件 在 WindowsXamlHost:在 WPF 中使用 UWP 的控件(Windows Community Toolkit) 一文中,我们说到了在 WPF 中引入简单的 UWP 控件以及相关的注意事项。
1439 0
|
C# 开发者
[UWP]新控件ColorPicker
原文:[UWP]新控件ColorPicker 1. 前言 Fall Creators Update中提供了一个新得ColorPicker控件,解决了以前选择颜色只能用Combo Box的窘境。 2. 一个简单的例子 如上所示,ColorPiker可以通过在光谱或色轮上拖动滑块,或者在RGB/HSV及十六进制的TextBox中直接输入颜色的数值改变Color属性。
1018 0
|
C# Windows
WPF实现抽屉效果
原文:WPF实现抽屉效果 界面代码(xaml): ...
1487 0
|
C# Android开发
Wpf 抽屉效果
原文:Wpf 抽屉效果 在android开发中有抽屉效果,就是在页面的边上有一个按钮,可以通过点击或者拖拽这个按钮,让页面显示。Wpf也可以实现相同的效果。   主要是通过一个DoubleAnimation和RectAnimation动画实现 ...
1380 0
|
JavaScript C#
wpf CefSharp 与 js交互
原文:wpf CefSharp 与 js交互 通过 NuGet 获取 CefSharp.WpF 组件。  xmlns:cefSharp="clr-namespace:CefSharp.
3829 0
|
API C# Windows
起调UWP的几种方法
原文:起调UWP的几种方法 由于种种原因吧,我需要使用一个WPF程序起调一个UWP程序,下面总结一下,给自己个备份。 启动UWP程序的关键是协议启动 给我们的UWP应用添加一个协议,like this: 然后使用协议启动该UWP有一下几种方式: 1.
988 0
|
UED 容器
[UWP]了解模板化控件(2.1):理解ContentControl
原文:[UWP]了解模板化控件(2.1):理解ContentControl UWP的UI主要由布局容器和内容控件(ContentControl)组成。布局容器是指Grid、StackPanel等继承自Panel,可以拥有多个子元素的类。
1191 0
|
C# 开发者
[UWP]了解模板化控件(5):VisualState
原文:[UWP]了解模板化控件(5):VisualState 1. 功能需求 使用TemplatePart实现上篇文章的两个需求(Header为空时隐藏HeaderContentPresenter,鼠标没有放在控件上时HeaderContentPresent半透明),虽然功能已经实现,但这样实现的话基本上也就别想扩展了。
1164 0
|
虚拟化 容器 .NET
[UWP]了解模板化控件(8):ItemsControl
原文:[UWP]了解模板化控件(8):ItemsControl 1. 模仿ItemsControl 顾名思义,ItemsControl是展示一组数据的控件,它是UWP UI系统中最重要的控件之一,和展示单一数据的ContentControl构成了UWP UI的绝大部分,ComboBox,ListBox,ListView,FlipView,GridView等控件都继承自ItemsControl。
1352 0
[UWP]了解模板化控件(5.2):UserControl vs. TemplatedControl
原文:[UWP]了解模板化控件(5.2):UserControl vs. TemplatedControl 1. UserControl vs. TemplatedControl 在UWP中自定义控件常常会遇到这个问题:使用UserControl还是TemplatedControl来自定义控件。
1478 0