如何应用TextBox控件模板?

5

我正在尝试简化一些代码并提高可维护性。

最初我想找到一种方法,使得一个文本框可以向左对齐,在达到最大值后不会在网格单元格内居中,并且可以收缩和扩展至最大值。

于是我开始编写了以下代码...

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="200" />
    </Grid.ColumnDefinitions>
    <TextBox Text="Some text"
             VerticalAlignment="Center"
             HorizontalAlignment="Stretch"
             Margin="10"
             MaxWidth="150" />
</Grid>

(代码清单1)

这样就得到了如下的结果...

居中对齐的文本框

(图1)

可以看到,这使文本框以其最大宽度居中,但它位于网格单元格的中心。

因此,我想也许如果我像这样更改文本框的水平对齐方式...

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="200" />
    </Grid.ColumnDefinitions>
    <TextBox  Text="Some text"
              VerticalAlignment="Center"
              HorizontalAlignment="Left"
              Margin="10"
              MaxWidth="100" />
</Grid>

(代码清单2)

但不幸的是,这只会得到像这样的东西...

左对齐的文本框过小

(图2)

同样,这并不正确,因为尽管右侧有很多空白空间,但文本框没有像我想要的那样扩展到其最大宽度100。

最终,我发现如果将文本框放置在嵌套网格中,则可以实现所需的效果。

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="200" />
    </Grid.ColumnDefinitions>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="1*"
                              MaxWidth="120" />
            <ColumnDefinition Width="0*" />
        </Grid.ColumnDefinitions>
        <TextBox Text="Some text"
             VerticalAlignment="Center"
             HorizontalAlignment="Stretch"
             Margin="10"
             MaxWidth="100" />
    </Grid>
</Grid>

(代码清单 3)

这将产生以下结果...

正确的文本框

(图 3)

正如您所看到的,嵌套网格填充了整个 200 宽的空间,但文本框填充了左侧的 100,并在右侧留下了空白空间。(如果此窗口可调整大小,则会相应地缩小和扩展)

我想将 代码清单 3 的方法应用于样式或控件模板中,以便我可以将其覆盖应用于适用此情况的 TextBoxes。

在完美的情况下,我会做一些像这样的事情...

<TextBox Text="Some text"
         VerticalAlignment="Center"
         HorizontalAlignment="Stretch"
         Margin="10"
         MaxWidth="100"
         Style="{StaticResource ExpandingTextBox}" /> 

(代码清单 4)

并且获得与 图3 相同的结果。

我尝试了以下方法...

<Style TargetType="{x:Type TextBox}"
       x:Key="ExpandingTextBox">
    <Setter Property="OverridesDefaultStyle"
            Value="True"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="TextBox">
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="1*"
                                          MaxWidth="170" />
                        <ColumnDefinition Width="0*" />
                    </Grid.ColumnDefinitions>
                    <ContentPresenter/>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

(代码清单 5)

但这会导致文本框从我的表单中完全消失。我不太擅长编写这样的模板,所以我有点茫然。

此外,我刚意识到嵌套列的宽度 = 最大宽度 + 2 x 边距。我不介意在 TextBox 声明中显式设置列宽,但如果可以自动完成,那就太棒了。

2个回答

9

您需要在模板中使用TextBox而不是ContentPresenter。您替换了TextBox的默认模板,但新模板没有文本输入区域。 请尝试像这样:

 <Style TargetType="{x:Type TextBox}" x:Key="ExpandingTextBox">
        <Setter Property="OverridesDefaultStyle" Value="True"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="TextBox">
                    <Grid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="1*"
                                      MaxWidth="170" />
                            <ColumnDefinition Width="0*" />
                        </Grid.ColumnDefinitions>
                        <TextBox  Text="{TemplateBinding Text}"
                                  VerticalAlignment="Center"
                                  HorizontalAlignment="Stretch"
                                  Margin="10"
                                  MaxWidth="100"
                         />
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

这应该可以工作,但在我看来有点奇怪。


谢谢你的帮助。使用你所写的内容,我确定了我的需求并在此基础上进行了扩展。请见下方我的答案帖子。 - imdandman

4

感谢jure帮我入门。但实际上,解决方案要比一开始看到的更加复杂。

让我们来看一下我做了什么。

在前一篇文章中提出的想法的基础上,经过一些调试,我得出了以下代码。

<Style TargetType="{x:Type TextBox}"
       x:Key="ExpandingTextBox">
    <Setter Property="OverridesDefaultStyle"
            Value="True" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="TextBox">
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="1*">
                            <ColumnDefinition.MaxWidth>
                                <Binding Path="MaxWidth"
                                         RelativeSource="{RelativeSource TemplatedParent}">
                                </Binding>
                            </ColumnDefinition.MaxWidth>
                        </ColumnDefinition>
                        <ColumnDefinition Width="0*" />
                    </Grid.ColumnDefinitions>
                    <TextBox  Text="{TemplateBinding Text}">
                        <TextBox.MaxWidth>
                            <Binding Path="MaxWidth"
                                     RelativeSource="{RelativeSource TemplatedParent}" />
                        </TextBox.MaxWidth>
                    </TextBox>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

(代码清单6)

当然,我在页面的XAML标记中应用了它...

<TextBox  Grid.Column="1"
          Grid.Row="3"
          Text="{Binding Path=Username}"
          Style="{StaticResource ExpandingTextBox}"
          Margin="10"
          MaxWidth="200" />

(代码清单7)

这将产生以下结果...

错误的样式模板

(图4)

看完这张图片并思考了一会儿,我明白了问题在于虽然我正确地在 style 中应用了最大宽度,但是最大宽度属性仍然被应用到 XAML 控件的根节点上。

基本上,它将其压缩到页面的最大宽度,然后在完成此操作之后,根据样式应用最大宽度!

如此简单,但是如此难以捉摸!

因此,我修改了样式,使用 Tag 属性来应用到样式化的最大宽度元素,因为 Tag 属性在根目录中不起作用。

<Style TargetType="{x:Type TextBox}"
       x:Key="ExpandingTextBoxMaxWidthInTag">
    <Setter Property="OverridesDefaultStyle"
            Value="True" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="TextBox">
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="1*">
                            <ColumnDefinition.MaxWidth>
                                <Binding Path="Tag"
                                         RelativeSource="{RelativeSource TemplatedParent}">
                                </Binding>
                            </ColumnDefinition.MaxWidth>
                        </ColumnDefinition>
                        <ColumnDefinition Width="0*" />
                    </Grid.ColumnDefinitions>
                    <TextBox  Text="{TemplateBinding Text}">
                        <TextBox.MaxWidth>
                            <Binding Path="Tag"
                                     RelativeSource="{RelativeSource TemplatedParent}" />
                        </TextBox.MaxWidth>
                    </TextBox>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

(代码清单8)

在XAML中应用如下...

<TextBox  Grid.Column="1"
          Grid.Row="3"
          Text="{Binding Path=Username}"
          Style="{StaticResource ExpandingTextBoxMaxWidthInTag}"
          Margin="10"
          Tag="200" />

最终的结果如下图所示...(见图5和6)。
因此,真正的诀窍在于意识到我不一定想限制整个模板控件的最大宽度,而只想限制控件内部的TextBox的宽度!
一旦我弄清了这一点,就有意义使用Tag属性,该属性没有对根级别的控件做任何事情!
我希望这能帮助其他可能遇到这个问题的人!

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接