如何应用TextBox控件模板?

时间:2013-04-25 18:47:38

标签: c# wpf xaml .net-4.0 controltemplate

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

我最初正在寻找一种方法,让文本框与左边对齐,可以收缩并扩展到最大值,而不会在达到最大值后在网格单元格内居中。

所以我开始写这样的代码......

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

(代码清单1)

这会产生这样的东西......

TextBox in Grid Centered

(图1)

正如您所看到的,这将TextBox的最大宽度居中,但它在网格单元的中心对齐。

那么我可能会因为我改变了文本框的水平对齐方式......

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

(代码清单2)

但不幸的是,这只会产生这样的东西......

Too small TextBox left aligned

(图2)

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

最终,我发现如果将TextBox放在嵌套网格中,您将获得所需的效果。

<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)

产生这个......

Correct looking TextBox

(图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)

但这只会导致文本框从我的表单中消失。我不太擅长写这样的模板,所以我有点不知所措。

另外,我刚刚意识到的一件事是嵌套列的宽度= MaxWidth + 2 x Margin。我不介意在TextBox声明中显式设置列宽,但如果它可以是自动的,那就太棒了。

2 个答案:

答案 0 :(得分:8)

您需要在模板中使用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>

这应该有效,但imo,有点奇怪......

答案 1 :(得分:3)

感谢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)

这给出了这样的结果......

Incorrect Style Template

(图4)

看了那张图片后,想了一会儿,我觉得问题在于即使我正确地在样式中应用了最大宽度,最大宽度属性仍然应用于在XAML中控制。

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

这么简单,但是难以捉摸!

因此,我修改了样式以使用Tag属性应用于样式化的max width元素,因为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" />

最后给出了这样的结果......

Desired Result with Layout Shown

(图5)

Desired Result without Layout Shown

(图6)

所以,真正的诀窍是意识到我不一定要限制整个模板化控件的最大宽度,而只是想限制TextBox内部控件的宽度!

一旦我想到这一点,只有使用Tag属性才有意义,该属性对根级别的控件没有做任何事情!

我希望这可以帮助其他可能遇到此问题的人!