UWP:没有 ControlTemplate 的按钮表现不同于一个
UWP: Button without ControlTemplate behaving different than with one
可能我从根本上理解了一些错误,但根据我所读的内容,ControlTemplate-属性 允许我完全定义控件的外观,而无需更改其逻辑(如事件等)。
为了进行一些测试,我创建了一个空的 UWP 项目,并创建了一个带有拉伸的按钮:
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Button HorizontalAlignment="Stretch"
Background="Red">
</Button>
</Grid>
不出所料,Button 贯穿整个 Window。
现在,我用基本上完全相同的按钮覆盖模板,但作为模板:
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Button>
<Button.Template>
<ControlTemplate>
<Button HorizontalAlignment="Stretch"
Background="Red" />
</ControlTemplate>
</Button.Template>
</Button>
</Grid>
在这种情况下,HorizontalAlignment 将被忽略。
因为我基本上用默认按钮模板(一个按钮)覆盖了按钮的外观,所以我期望有相同的行为。我可以通过在 Button 本身上定义 HorizontalAlignment 然后使用 TemplateBinding 来修复它,但我不明白为什么它没有按预期被覆盖。
如果我们查看 Button 的默认模板,它本质上是这样的(省略了一些诸如视觉状态的内容):
<Grid>
<ContentPresenter/>
</Grid>
检查实时可视化树中的按钮可确认:
因此您看到 Grid 没有替换 Button 元素,Button 仍然是可视化树中的一个 Button,而是模板定义了直接 child 的按钮将是。
如果您将模板设置为以下内容:
<Button HorizontalAlignment="Stretch" Background="Red"/>
然后你会得到这个视觉树:
HorizontalAlignment 无法以这种方式工作的原因是因为它应用于内部 Button 而不是外部按钮。作为布局过程的一部分,外部按钮 (parent) 决定内部按钮 (child) 的大小和位置,并且由于 parent 未拉伸, 它只能容纳显示 child.
所需的最小尺寸
这就像你正在尝试这样做:
<Grid>
<Button>
<!-- This button won't stretch to the width of the Grid -->
<Button HorizontalAlignment="Stretch"/>
<Button>
</Grid>
把一个Button的Template设置成一个Button是没有意义的。看起来你想要做的是设置一个样式,然后你可以为模板设置一个 setter 和 parent.
的 HorizontalAlignment
<Style x:Name="MyCustomButton" TargetType="Button">
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
....
</ControlTemplate>
</Setter>
</Setter>
</Style>
如果要调整控件的模板,可以在 XAML 设计器中右键单击它 > 编辑模板 > 编辑副本。这会将该控件的默认样式引入您的页面,然后您可以对其进行修改。
编辑
So, every Control, even without Template, has an appearance in the VisualTree and thus needs to be targeted?
我不确定您所说的 "targeted" 是什么意思,但是是的,每个控件和 Control-derived class 都有一个可以指定的模板。如果您对这些样式感兴趣,每个控件都有一个 default style which sets the Template for the control (among other properties). I refer you to Default control styles and templates。
The Template then only overrides the "Content" of the Control?
没有。显示内容的 ContentControl (such as a Button) is different from the Template. Think of the Template as defining the entire visual appearance of the control, and the Content as defining only a subset of the appearance within the template somewhere. When you set the Content of a Button, for example, you're not changing the entire appearance of the button (the button still has a border and background), you only changed what's displayed inside the button at some particular point within the button's visual tree. It is the ContentPresenter 控件的内容(请参阅我的第一个屏幕截图)。
I didn't know I can see the Visual Tree, do I need a special version or such for that?
Visual Studio 具有实时可视化树功能已有一段时间(至少自 2015 年以来),因此除非您拥有 Visual Studio.[=22 的过时版本,否则您应该拥有该功能=]
可能我从根本上理解了一些错误,但根据我所读的内容,ControlTemplate-属性 允许我完全定义控件的外观,而无需更改其逻辑(如事件等)。
为了进行一些测试,我创建了一个空的 UWP 项目,并创建了一个带有拉伸的按钮:
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Button HorizontalAlignment="Stretch"
Background="Red">
</Button>
</Grid>
不出所料,Button 贯穿整个 Window。 现在,我用基本上完全相同的按钮覆盖模板,但作为模板:
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Button>
<Button.Template>
<ControlTemplate>
<Button HorizontalAlignment="Stretch"
Background="Red" />
</ControlTemplate>
</Button.Template>
</Button>
</Grid>
在这种情况下,HorizontalAlignment 将被忽略。 因为我基本上用默认按钮模板(一个按钮)覆盖了按钮的外观,所以我期望有相同的行为。我可以通过在 Button 本身上定义 HorizontalAlignment 然后使用 TemplateBinding 来修复它,但我不明白为什么它没有按预期被覆盖。
如果我们查看 Button 的默认模板,它本质上是这样的(省略了一些诸如视觉状态的内容):
<Grid>
<ContentPresenter/>
</Grid>
检查实时可视化树中的按钮可确认:
因此您看到 Grid 没有替换 Button 元素,Button 仍然是可视化树中的一个 Button,而是模板定义了直接 child 的按钮将是。
如果您将模板设置为以下内容:
<Button HorizontalAlignment="Stretch" Background="Red"/>
然后你会得到这个视觉树:
HorizontalAlignment 无法以这种方式工作的原因是因为它应用于内部 Button 而不是外部按钮。作为布局过程的一部分,外部按钮 (parent) 决定内部按钮 (child) 的大小和位置,并且由于 parent 未拉伸, 它只能容纳显示 child.
所需的最小尺寸这就像你正在尝试这样做:
<Grid>
<Button>
<!-- This button won't stretch to the width of the Grid -->
<Button HorizontalAlignment="Stretch"/>
<Button>
</Grid>
把一个Button的Template设置成一个Button是没有意义的。看起来你想要做的是设置一个样式,然后你可以为模板设置一个 setter 和 parent.
的 HorizontalAlignment<Style x:Name="MyCustomButton" TargetType="Button">
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
....
</ControlTemplate>
</Setter>
</Setter>
</Style>
如果要调整控件的模板,可以在 XAML 设计器中右键单击它 > 编辑模板 > 编辑副本。这会将该控件的默认样式引入您的页面,然后您可以对其进行修改。
编辑
So, every Control, even without Template, has an appearance in the VisualTree and thus needs to be targeted?
我不确定您所说的 "targeted" 是什么意思,但是是的,每个控件和 Control-derived class 都有一个可以指定的模板。如果您对这些样式感兴趣,每个控件都有一个 default style which sets the Template for the control (among other properties). I refer you to Default control styles and templates。
The Template then only overrides the "Content" of the Control?
没有。显示内容的 ContentControl (such as a Button) is different from the Template. Think of the Template as defining the entire visual appearance of the control, and the Content as defining only a subset of the appearance within the template somewhere. When you set the Content of a Button, for example, you're not changing the entire appearance of the button (the button still has a border and background), you only changed what's displayed inside the button at some particular point within the button's visual tree. It is the ContentPresenter 控件的内容(请参阅我的第一个屏幕截图)。
I didn't know I can see the Visual Tree, do I need a special version or such for that?
Visual Studio 具有实时可视化树功能已有一段时间(至少自 2015 年以来),因此除非您拥有 Visual Studio.[=22 的过时版本,否则您应该拥有该功能=]