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

Posted

技术标签:

【中文标题】为啥我不能在我的 UserControl 中重置 TextBox 的背景?【英文标题】:Why can't I reset the Background of the TextBox in my UserControl?为什么我不能在我的 UserControl 中重置 TextBox 的背景? 【发布时间】:2011-01-16 01:48:18 【问题描述】:

我构建了一个 UserControl,它以有趣且有用的方式扩展了 ComboBox 的功能。掉下来的样子是这样的:

我已经在控件中构建了一大堆功能,它们都可以顺利运行。这让我相信我对自己在做什么有一些线索。您会认为让 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 本身仍然是白色的。

在 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 的背景画笔设置为样式的设置器之一,则不会发生任何事情。如果我在 PART_EditableTextBox 上明确设置背景,则不会发生任何事情。 (我可以设置它的 Foreground 或 FontFamily,它们工作得很好。)

如果我将该 ScrollViewer 的背景显式设置为绿色,那么,瞧,TextBox 会变为绿色。

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

除非我能弄清楚为什么 TextBox 忽略了它的背景,否则我必须忍受调整 ScrollViewer。那么如何从用户控件的 EditableTextBoxBackground 属性中获取画笔呢?我已经将此作为一个依赖属性,当它发生更改时会正确引发 PropertyChanged 事件。我在神秘的 ScrollViewer 的 XAML 中绑定到它,如下所示:

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

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

所以这就是我所知道的:我的 UserControl 的属性设置正确。 TextBox 样式的绑定正确绑定到 UserControl 的属性。 TextBox 的控件模板中 ScrollViewer 上的绑定绑定到 right 属性。该属性在更改时使用正确的属性名称引发 PropertyChanged,并且绑定将值推送到 ScrollViewer 背景属性。

什么也没发生。

所以我想我有一个三部分的问题:1)为什么? 2) ScrollViewer 到底在做什么?我有我的怀疑,但现在是早上的一个,我越来越难以表达它们。 3) 为什么 Blend 给了我一个不同的控制模板来使用,而不是更容易理解的 here?

真的,任何帮助将不胜感激。

【问题讨论】:

“你会认为让 UserControl 的样式设置可编辑的 TextBox 的背景画笔是一件小事。事实上,这似乎是不可能的。” - 大声笑,说得好。我现在也遇到了同样的事情。 【参考方案1】:

你有问题。我有答案。

1- 为什么 ScrollViewer 的 Background 绑定行为如此奇怪?

当第一次测量TextBox 时,它会实例化其模板。这将创建 ScrollViewer。应用模板后,TextBox 检查ScrollViewerBackground 属性当前是否具有空值。如果是这样,它将用Background.Transparent 覆盖它。这样做会断开您的绑定。

这就是为什么当你在构造函数中设置它而不是稍后它起作用的原因:TextBox 看到 null 值并用 Background.Transparent 覆盖它,破坏了绑定。

2- ScrollViewer 在那里做什么?

TextBox 是一个 Control,它实际上并没有处理文本呈现本身的任何血腥细节 - 如果您浏览可视化树,您会看到这是由另一个名称为 something 的 Visual 处理的像“文本视图”。 TextBox 的主要工作实际上是呈现文本框周围的边框和/或让您给它一个全新的外观。

TextBox 需要一个具有名为 PART_ContentHost 的元素的模板,该元素是 ContentPresenterScrollViewer。如果是简单的ContentPresenter,则简单地将内部“TextView”对象添加到其中。如果是ScrollViewerTextBox 还会连接一些额外的功能,例如在获得焦点时将文本滚动到视图中。

ScrollViewer的生活目的是让TextBox中的文字可以水平滚动,对于多行文本框也可以垂直滚动。

3- 为什么 Blend 给了我不同的控制模板

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

我又添加了两个相关问题:

4- 为什么设置 TextBox 的 Background 属性不起作用?

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

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

如果您对白边没问题,您可以简单地将ScrollViewer 包裹在&lt;Border&gt; 中,并在&lt;Border&gt; 上设置背景。如果没有,您将不得不将所需的背景移动到主 ControlTemplate 中为 ComboBox 提供的 chrome 中。

【讨论】:

哇。有没有一本书详细介绍了这样的事情,或者这是基于经验+反射器?

以上是关于为啥我不能在我的 UserControl 中重置 TextBox 的背景?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我不能在同一个程序集中使用 UserControl 上的 Name 属性?

为啥 UserControl 的 Unloaded 事件不会触发?

如何将我的UserControl从另一个项目(Dll)添加到我的WPF解决方案?

为啥我不能在我的 docker 容器中运行 phpinfo()?

为啥我不能在我的 selenium 脚本中使用 executeScript?

为啥我不能在我的类中内联函数? [复制]