WPF 按钮如何允许在不丢失其核心外观的情况下设置内容?

How does a WPF button allow the content to be set without losing its core appearance?

这是一个有点愚蠢的问题,但也是一个让我困惑的问题,它是如何工作的。假设我定义了自己的 UserControl,它扩展了 ContentControl 并提供了这个:

<Grid x:Name="William">

    <Grid.RowDefinitions>
        <RowDefinition Height="*"/>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>
    <TextBox Text="{Binding RelativeSource={RelativeSource Self}, Path=FontSize, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"></TextBox>
    <Button HorizontalAlignment="Center"  Content="{Binding Time}" Grid.Row="1" CommandTarget="{Binding RelativeSource={RelativeSource AncestorType=Grid}}" Command="{x:Static local:Commands.Foo}">
    </Button>
</Grid>

忽略绑定,重要的是这是我的 UserControl 的内容。

我在主 Window 中创建了一个用户控件实例,如下所示:

<local:UserControl1 x:Name="myC" Grid.Row="1" DataContext="{Binding ChildName}">
    </local:UserControl1>

一切正常。现在我可以像这样覆盖用户控件中定义的内容:

<local:UserControl1 x:Name="myC" Grid.Row="1" DataContext="{Binding ChildName}">
Hello!
    </local:UserControl1>

并且整个内容被替换为文本 'Hello!'

但是一个按钮允许我设置内容,但仍然保留一个按钮:

<Button x:Name="testBt">
        Hello!
    </Button>

谁能解释这是为什么?

已完全控制更新:

<UserControl
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
         xmlns:local="clr-namespace:RoutedCommandDemo"
         xmlns:dates="clr-namespace:System;assembly=mscorlib" xmlns:Linq="clr-namespace:System.Xml.Linq;assembly=System.Xml.Linq" x:Class="RoutedCommandDemo.UserControl1" 
         mc:Ignorable="d" 
         d:DesignHeight="300" d:DesignWidth="300"
         >

<UserControl.Resources>

    <Style TargetType="Button">
        <Style.Setters>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="Button">
                        <Grid>
                            <Border Background="{TemplateBinding BorderBrush}" Margin="5 5 0 0" />
                            <Border BorderBrush="Black" BorderThickness="1"
            Background="{TemplateBinding Background}" Margin="0 0 5 5">
                                <ContentPresenter HorizontalAlignment="{TemplateBinding Property=HorizontalAlignment}"
                                      VerticalAlignment="Center"/>

                            </Border>
                        </Grid>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
            <Setter Property="Background" Value="Yellow"></Setter>
            <Setter Property="BorderBrush" Value="Red"></Setter>
        </Style.Setters>
        <Style.Triggers>
            <Trigger Property="IsMouseOver" Value="True">
                <Setter Property="Background"  Value="Pink"></Setter>
                <Setter Property="BorderBrush" Value="Green"></Setter>
            </Trigger>
        </Style.Triggers>
    </Style>
    <Style TargetType="local:UserControl1">
        <Style.Setters>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="local:UserControl1">
                        <Grid>
                            <Grid.RowDefinitions>
                                <RowDefinition Height="*"></RowDefinition>
                                <RowDefinition Height="*"></RowDefinition>
                                <RowDefinition Height="*"></RowDefinition>
                            </Grid.RowDefinitions>

                            <ContentPresenter Content="{TemplateBinding Content}" HorizontalAlignment="{TemplateBinding Property=HorizontalAlignment}"
                                      VerticalAlignment="Center"/>

                            <TextBox Grid.Row="1" Text="{Binding RelativeSource={RelativeSource Self}, Path=FontSize, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"></TextBox>
                            <Button Grid.Row="2" HorizontalAlignment="Center"  Content="{Binding Time}" CommandTarget="{Binding RelativeSource={RelativeSource AncestorType=Grid}}" Command="{x:Static local:Commands.Foo}">
                            </Button>
                        </Grid>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style.Setters>
    </Style>
</UserControl.Resources>


<UserControl.CommandBindings>
    <CommandBinding Command="{x:Static local:Commands.Foo}" Executed="CommandBinding_Executed"/>

</UserControl.CommandBindings>
<UserControl.InputBindings>
    <KeyBinding Command="{x:Static local:Commands.Foo}" Modifiers="Control"  Key="Space"></KeyBinding>
</UserControl.InputBindings>

按钮有一个控件模板

看看这个 MSDN 页面:Button Styles and Templates。它列出了默认按钮样式。

注意以下几点:

<Setter Property="Template">
    <Setter.Value>
        <ControlTemplate TargetType="Button">
            <Grid>
                ...
                <Border x:Name="Background" ...>
                    ...
                </Border>
                <ControlPresenter Content="{TemplateBinding Content}" ... />
                <Rectangle x:Name="DisabledVisualElement" ... />
                <Rectangle x:Name="FocusVisualElement" ... />
                ...

您在 "Hello" 文本后面看到的是 Background Border 元素。内容本身由 ContentPresenter.

呈现

视觉主题定义了这些默认样式(因此您可以在 WinXP/Win7/Win10 等上获得不同的外观)。它们也可以被应用程序样式覆盖。

由于显而易见的原因,UserControl 没有预定义的外观。

有关详细信息,请参阅 here or here