WPF ControlTemplate交互

本文介绍了WPF中如何使用ControlTemplate控制控件的外观,避免代码和UI的耦合。通过示例展示了如何创建自定义控件MyExpander,利用DependencyProperty和RelativeSource绑定实现双向交互,以及在ControlTemplate中使用Triggers和VisualState来响应属性变化,动态调整控件状态。同时探讨了何时使用TemplatePart、Trigger和VisualState。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

PF的UI框架,用户可以轻松地使用代码控制控件的外观。一个控件在鼠标进入的时候背景变成蓝色,下面这段代码实现:

protected override void OnMouseEnter(MouseEventArgs e)

{

    base.OnMouseEnter(e);

    Background = new SolidColorBrush(Colors.Blue);

}

但是这样做代码和UI过于耦合,难以扩展。应该是使用代码告诉ControlTemplate去改变外观,或者控制ControlTemplate中可用的元素进入某个状态。

MyExpander是一个HeaderedContentControl,它包含一个IsExpanded用于指示当前是展开还是折叠。ControlTemplate中包含ExpanderToggleButton及ContentPresenter两个元素。

代码:

public class MyExpander : HeaderedContentControl

{

    public MyExpander()

    {

        DefaultStyleKey = typeof(MyExpander);

    }

    public bool IsExpanded

    {

        get => (bool)GetValue(IsExpandedProperty);

        set => SetValue(IsExpandedProperty, value);

    }

    public static readonly DependencyProperty IsExpandedProperty =

        DependencyProperty.Register(nameof(IsExpanded), typeof(bool), typeof(MyExpander), new PropertyMetadata(default(bool), OnIsExpandedChanged));

 

    protected virtual void OnIsExpandedChanged(bool oldValue, bool newValue)

    {

        if (newValue)

            OnExpanded();

        else

            OnCollapsed();

    }

通常ControlTemplate中元素都通过TemplateBinding获取控件的属性值。但需要双向绑定的话,就是RelativeSource

RelativeSource有几种模式,分别是:

FindAncestor: 引用数据绑定元素的父链中的上级。 这可用于绑定到特定类型的上级或其子类。

PreviousData: 允许在当前显示的数据项列表中绑定上一个数据项(不是包含数据项的控件)。

Self: 引用正在其上设置绑定的元素,并允许你将该元素的一个属性绑定到同一元素的其他属性上。

emplatedParent: 引用应用了模板的元素,其中此模板中存在数据绑定元素。。

 

ControlTemplate中主要使用RelativeSource Mode=TemplatedParent的Binding,它相当于TemplateBinding的双向绑定版本。,主要是为了可以和控件本身进行双向绑定。ExpanderToggleButton.IsChecked使用这种绑定与Expander的IsExpanded关联,当Expander.IsChecked为True时ExpanderToggleButton处于选中的状态。

代码:

IsChecked="{Binding IsExpanded,RedlativeSource={RelativeSource

Mode= TemplatedParent},Mode=TwoWay}"

可以为ControlTemplate添加Triggers,内容为TriggerEventTrigger的集合,Triggers通过响应属性值变更或事件更改控件的外观。

代码:

<ControlTemplate TargetType="{x:Type local:ExpanderUsingTrigger}">

    <Border Background="{TemplateBinding Background}">

    </Border>

    <ControlTemplate.Triggers>

        <Trigger Property="IsExpanded"

                 Value="True">

            <Setter Property="Visibility"

                    TargetName="ContentPresenter"

                    Value="Visible" />

        </Trigger>

    </ControlTemplate.Triggers>

</ControlTemplate>

为了表明控件期待在ControlTemplate存在某个特定部件,防止编辑ControlTemplate的开发人员删除它,控件上会添加添加TemplatePartAttribute协定。

[TemplatePart(Name =ContentPresenterName,Type =typeof(UIElement))]

意思是期待在ControlTemplate中存在名称为 "ContentPresenterName",类型为UIElement的部件。

 

TemplateVisualStateAttribute协定:

自定义控件可以使用TemplateVisualStateAttribute协定声明它的VisualState,用于通知控件的使用者有这些VisualState可用。

[TemplateVisualState(Name = StateExpanded, GroupName = GroupExpansion)]

[TemplateVisualState(Name = StateCollapsed, GroupName = GroupExpansion)]

TemplateVisualStateAttribute是可选的,而且就算控件声明了这些VisualState,ControlTemplate也可以不包含它们中的任何一个,并且不会引发异常。

 

需要向控件发出命令的,如响应点击事件,就用TemplatePart。

简单的UI,如隐藏/显示某个元素就用Trigger。

如果要有动画,并且代码量和使用Trigger的话,我会选择用VisualState。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值