[WPF] 如何在自定义控件里面访问子控件

984 阅读1分钟

为建立中文知识库加块砖        ——中科大胡不归

问题描述

HeaderedContentControl是一个高度类似GroupBox的WPF组件。我们要实现点击Header,隐藏和显示Content的效果。

先看效果

问题分析

原生的HeaderedContentControl并不支持我们的需求,所以我们需要继承它并响应点击事件。在用户点击时,将Content的Visibility由Visible设置为Collapsed。对目前的我,最大的难点是如何在自定义控件里面访问子控件。

解决方案

通过参考文章中的方法,控件+模板的配合实现。

定义模板

<Style TargetType="control:MyHeaderedContentControl">
    <!-- 此处的Margin的bottom是针对下个同级Control的 -->
    <Setter Property="Margin" Value="0,0,0,0" />
    <Setter Property="IsTabStop" Value="False" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="control:MyHeaderedContentControl">
                <StackPanel>
                    <!-- Header -->
                    <StackPanel Orientation="Horizontal" Margin="13,6,0,6" Height="40">
                            <TextBlock Width="6" Background="{DynamicResource SystemControlBackgroundAccentBrush}"/>
                            <TextBlock Text="{TemplateBinding Header}" FontSize="14" Foreground="{DynamicResource SystemControlForegroundBaseHighBrush}"
                                       Style="{DynamicResource BaseTextBlockStyle}"
                                       HorizontalAlignment="Center" VerticalAlignment="Center"
                                       Margin="6,0,0,0"/>
                    </StackPanel>
                    <!-- Content -->
                    <ContentPresenter x:Name="PART_Content" Content="{TemplateBinding Content}"/>
                </StackPanel>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

定义Control

[TemplatePart(Name = "PART_Content", Type = typeof(ContentPresenter))]
public class MyHeaderedContentControl : HeaderedContentControl
{
   private ContentPresenter _contentPresenter;

   public static readonly DependencyProperty ClickToHideProperty =
      DependencyProperty.Register("ClickToHide", typeof(bool), typeof(MyHeaderedContentControl),
         new PropertyMetadata(null));

   public bool ClickToHide
   {
      get => (bool)GetValue(ClickToHideProperty);
      set => SetValue(ClickToHideProperty, value);
   }

   public override void OnApplyTemplate()
   {
      base.OnApplyTemplate();
      _contentPresenter = GetTemplateChild("PART_Content") as ContentPresenter;
   }

   protected override void OnPreviewMouseLeftButtonDown(MouseButtonEventArgs e)
   {
      base.OnPreviewMouseLeftButtonDown(e);

      // Only a click on this control is handled, not children.
      if (_contentPresenter is null || !e.Source.Equals(this))
         return;

      // If the child (Content) is visible, collapse it, if it is collapsed, make it visible.
      _contentPresenter.Visibility = _contentPresenter.Visibility == Visibility.Collapsed
            ? Visibility.Visible : Visibility.Collapsed;

      e.Handled = true;
   }
}

实例引用

<Grid>
...
    <control:MyHeaderedContentControl Header="管理通知" 
                                      VerticalAlignment="Top" 
                                      ClickToHide="True"
                                      Grid.Row="0" >
...
</Grid>

参考文章

  1. How to change visibility of child control in my customized Control?
  2. Creating a Control That Has a Customizable Appearance