为什么我不能在UserControl中重置TextBox的背景?

时间:2010-02-12 09:00:56

标签: wpf templates user-controls controls styles

我构建了一个UserControl,它以有趣和有用的方式扩展了ComboBox的功能。当它下降时看起来像这样:

My user control

我在控件中构建了一大堆功能,它们都能顺利运行。这让我相信我对于我正在做的事情有一些线索。你认为让UserControl的样式设置可编辑的TextBox的背景画笔是一件小事。事实上,这似乎是不可能的。我很困惑。

UserControl的XAML,缩写(你会为此感谢我),看起来像这样:

<UserControl x:Class="MyApp.CodeLookupBox" x:Name="MainControl">
    <UserControl.Resources>
       <!-- tons of DataTemplates and Styles, most notably the style that
            contains the control template for the ComboBox -->
    <UserControl.Resources>
    <ComboBox x:Name="ComboBox" 
                   Margin="0" 
                   Style="{DynamicResource ComboBoxStyle1}" 
                   VerticalAlignment="Top"
                   ItemTemplate="{StaticResource GridViewStyleTemplate}"/>
</UserControl>

在这个控件中有很多代码隐藏,主要是依赖属性,我用它来选择下拉列表中使用的模板。

让我疯狂的是可编辑的文本框。我希望能够从用户控件的样式设置其背景画笔 - 例如,当我在我的XAML中声明其中一个用户控件时,它使用如下样式:

<Style TargetType="{x:Type local:CodeLookupBox}">
    <Style.Triggers>
        <DataTrigger Binding="{Binding IsRequired}" Value="True">
            <Setter Property="EditableTextBoxBackground" Value="{StaticResource RequiredFieldBrush}"/>
        </DataTrigger>
    </Style.Triggers>
</Style>

我开始简单地设置UserControl的背景,但是将背景设置为可编辑的TextBox。 TextBox本身仍然是白色的。

在ComboBox的模板中,有一个控制TextBox的样式:

<Style x:Key="ComboBoxEditableTextBox" TargetType="{x:Type TextBox}">
    <Setter Property="OverridesDefaultStyle" Value="true"/>
    <Setter Property="AllowDrop" Value="true"/>
    <Setter Property="MinWidth" Value="0"/>
    <Setter Property="MinHeight" Value="0"/>
    <Setter Property="FocusVisualStyle" Value="{x:Null}"/>

    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type TextBox}">
                <ScrollViewer 
                            x:Name="PART_ContentHost" 
                            Focusable="false" 
                            HorizontalScrollBarVisibility="Hidden" 
                            VerticalScrollBarVisibility="Hidden"/>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

还有TextBox(在ComboBox的控件模板中)其糟糕的自我:

<TextBox 
    x:Name="PART_EditableTextBox" 
    Margin="{TemplateBinding Padding}" 
    Style="{StaticResource ComboBoxEditableTextBox}" 
    HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" 
    VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
    IsReadOnly="{Binding IsReadOnly, RelativeSource={RelativeSource TemplatedParent}}"/>

现在,有一个关于ComboBoxEditableTextBox样式的人无法理解的事物的明确要素。 ScrollViewer在那里做什么?我不知道。我可以告诉你,如果我注释掉设置TextBox的ControlTemplate的样式部分,就会发生非常糟糕的事情。

我也知道这一点:如果我明确地将TextBox的Background画笔设置为样式的setter之一,则没有任何反应。如果我在PART_EditableTextBox上显式设置背景,则没有任何反应。 (我可以设置它的Foreground,或者它的FontFamily,它们工作正常。)

如果我明确地将ScrollViewer的背景设置为绿色,那么,文本框会变为绿色。

好的,所以TextBox忽略了自己的Background,并使用了它的控件模板中的一个。实际上,严格来说,它不是使用控制模板中的那个。当我设置ScrollViewer的背景时,颜色边缘周围有一个确定的边距,而不是完全填充TextBox的颜色。但是这个边距是白色的,而不是背景色。

除非我能弄清楚为什么TextBox忽略了它的背景,否则我不得不调整ScrollViewer。那么如何从用户控件的EditableTextBoxBackground属性中获取它呢?我已经使它成为一个依赖属性,它在更改时正确引发了PropertyChanged事件。我在神秘的ScrollViewer的XAML中绑定它,如下所示:

Background="{Binding ElementName=MainControl, 
    Path=EditableTextBoxBackground, 
    Converter={StaticResource DebuggingConverter}}"

我在调试转换器中设置了一个断点。首次绘制控件时,它会被击中两次。第一次,画笔的值为null。第二次,它是正确的价值。如果我在UserControl的构造函数中设置属性,它就可以工作。

所以这就是我所知道的:我的UserControl的属性设置正确。 TextBox样式的绑定正确绑定到UserControl的属性。 TextBox控件模板中ScrollViewer上的绑定绑定到右侧属性。 Property更改时,属性会使用正确的属性名称引发PropertyChanged,绑定会将值推送到ScrollViewer Background属性。

没有任何反应。

所以我猜我有三个问题:1)为什么? 2)ScrollViewer首先在那里做什么?我有我的怀疑,但它是早上的一个,而且我很难说清楚它们。 3)为什么Blend给我一个不同的控制模板,而不是更容易理解的模板here

真的,任何帮助都会受到赞赏。

1 个答案:

答案 0 :(得分:6)

你有疑问。我有答案。

1-为什么ScrollViewer的背景绑定表现得如此奇怪?

首次测量TextBox时,会实例化其模板。这会创建ScrollViewer。应用模板后,TextBox会检查ScrollViewer的{​​{1}}属性当前是否为空值。如果是,则用Background覆盖它。这样做会断开您的绑定。

这就是为什么它在你在构造函数中设置时有效但后来没有:TextBox看到null值并用Background.Transparent覆盖它,打破了绑定。

2-无论如何,ScrollViewer在那里做什么?

Background.Transparent是一个TextBox,它实际上并不处理文本表示本身的任何细节 - 如果你浏览可视树,你会看到这是由另一个{{1名称类似于“TextView”的名称。 Control的主要工作实际上是在文本框周围显示边框和/或让您给它一个全新的外观。

Visual需要一个名为TextBox的元素的模板,该元素可以是TextBoxPART_ContentHost。如果它是一个简单的ContentPresenter,则只需添加内部“TextView”对象即可。如果它是ScrollViewer,则ContentPresenter还会附加一些其他功能,例如在聚焦时将文本滚动到视图中。

ScrollViewer生活中的目的是允许TextBox中的文字水平滚动,还可以垂直滚动多行文字框。

3-为什么Blend会给我一个不同的控制模板

Blend从引用的程序集中加载实际的ScrollViewer XAML,在本例中为PresentationFramework.dll以及当前系统主题的相关主题dll。因此,它将在您安装的.NET Framework版本中加载实际使用的内容。您链接的站点上的XAML只是示例代码,而不是实际的.NET Framework XAML。

我添加了两个更相关的问题:

4-为什么没有设置TextBox的Background属性?

WPF的TextBox子类都没有实际实现自己的ControlTemplate属性。 Control DependencyProperty只是一个命名画笔,Control的模板可以绑定到,如果它喜欢Background与任何其他Background一样正确。默认的TextBox模板包含一个“chrome”对象,其中包含用于显示背景的代码,类似于您可能使用边框的代码。由于Control已经显示了自己的“chrome”,因此它使用自己的TextBox模板,其中包含ComboBox但不包含周围的chrome。这就是为什么在TextBox内的ScrollViewer上设置Background属性无效。

5-如何解决我的问题并在ComboBox中绑定TextBox的背景颜色

如果您对白色边距感到满意,可以将TextBox内部包裹在ComboBox内,并在ScrollViewer上设置背景。如果没有,则必须将所需背景移动到<Border>的主<Border>中提供的镶边中。