Rob*_*ney 3 wpf user-controls controls templates styles
我已经构建了一个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>
Run Code Online (Sandbox Code Playgroud)
在这个控件中有很多代码隐藏,主要是依赖属性,我用它来选择下拉列表中使用的模板.
让我疯狂的是可编辑的文本框.我希望能够从用户控件的样式设置其背景画笔 - 例如,当我在我的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>
Run Code Online (Sandbox Code Playgroud)
我开始简单地设置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>
Run Code Online (Sandbox Code Playgroud)
还有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}}"/>
Run Code Online (Sandbox Code Playgroud)
现在,有一个关于ComboBoxEditableTextBox风格的人无法了解的事物的明确要素.ScrollViewer在那里做什么?我不知道.我可以告诉你,如果我注释掉设置TextBox的ControlTemplate的样式部分,就会发生非常糟糕的事情.
我也知道这一点:如果我明确地将TextBox的Background画笔设置为样式的setter之一,则没有任何反应.如果我在PART_EditableTextBox上显式设置背景,则没有任何反应.(我可以设置它的Foreground,或者它的FontFamily,它们工作得很好.)
但是,如果我将ScrollViewer的背景显式设置为绿色,那么,TextBox会变为绿色.
好的,所以TextBox忽略了自己的Background,并使用了它的控件模板中的一个.实际上,严格来说,它不是使用控制模板中的那个.当我设置ScrollViewer的背景时,颜色边缘周围有一个确定的边距,而不是完全填充TextBox的颜色.但是这个边距是白色的,而不是背景色.
除非我能弄清楚为什么TextBox忽略了它的背景,否则我不得不调整ScrollViewer.那么如何从用户控件的EditableTextBoxBackground属性中获取它呢?我已经使它成为一个依赖属性,它在更改时正确引发了PropertyChanged事件.我在神秘的ScrollViewer的XAML中绑定它,如下所示:
Background="{Binding ElementName=MainControl,
Path=EditableTextBoxBackground,
Converter={StaticResource DebuggingConverter}}"
Run Code Online (Sandbox Code Playgroud)
我在调试转换器中设置了一个断点.首次绘制控件时,它会被击中两次.第一次,画笔的值为null.第二次,它是正确的价值.如果我在UserControl的构造函数中设置属性,它就可以工作.
所以这就是我所知道的:我的UserControl的属性设置正确.TextBox样式的绑定正确绑定到UserControl的属性.TextBox控件模板中ScrollViewer上的绑定绑定到右侧属性.PropertyChanged在更改时使用正确的属性名称引发,并且绑定会将值推送到ScrollViewer Background属性.
没有任何反应.
所以我想我有三个问题:1)为什么?2)ScrollViewer首先在那里做什么?我怀疑,但它是早上的一个,我很难说清楚它们.3)为什么Blend给我一个不同的控制模板来使用,而不是在这里找到更易于理解的模板?
真的,任何帮助将不胜感激.
你有疑问.我有答案.
1-为什么ScrollViewer的背景绑定行为如此奇怪?
当TextBox首先测量,它实例它的模板.这创造了ScrollViewer.一旦应用模板时,将TextBox看到会检查ScrollViewer的Background财产目前有一个空值.如果是这样,它将覆盖它Background.Transparent.这样做会断开您的绑定.
这就是为什么它在构造函数中设置它而不是稍后的原因:TextBox看到null值并用Background.Transparent覆盖它,打破了绑定.
2-无论如何,ScrollViewer在那里做什么?
TextBox是一种Control实际上不处理任何的文字呈现自身的血淋淋的细节-如果你浏览的可视化树,你会看到,这是由另一个处理Visual与名称类似"的TextView". TextBox实际上,主要工作是在文本框周围显示边框和/或让它给它一个全新的外观.
TextBox需要一个名为elemnent的模板,PART_ContentHost它是a ContentPresenter或a ScrollViewer.如果它很简单ContentPresenter,只需添加内部"TextView"对象即可.如果它是a ScrollViewer,它TextBox还会连接一些额外的功能,例如在聚焦时将文本滚动到视图中.
在ScrollViewer人生的目的是为了让在文本TextBox横向滚动,同时也为垂直多文本框滚动.
3-为什么Blend给我一个不同的控制模板
Blend ControlTemplate从引用的程序集加载实际的XAML,在本例中为PresentationFramework.dll和当前系统主题的关联主题dll.因此,它将加载您已安装的.NET Framework版本中实际使用的内容.您链接的站点上的XAML只是示例代码,而不是实际的.NET Framework XAML.
我添加了两个更相关的问题:
4-为什么没有设置TextBox的Background属性?
WPF的Control子类实际上都没有实现它们的Background属性.该BackgroundDependencyProperty的仅仅是一个控制的模板可以绑定到一个叫刷,如果它喜欢.这TextBox对任何其他人来说都是如此Control.默认TextBox模板包含一个"chrome"对象,其中包含用于显示背景的代码,类似于您可能使用边框的代码.由于ComboBox已经显示了自己的"chrome",它使用自己的TextBox模板,包括ScrollViewer但不包括周围的chrome.这就是为什么Background在TextBox内部设置属性ComboBox没有效果.
5-如何解决我的问题并在ComboBox中绑定TextBox的背景颜色
如果您对白色边缘没问题,您可以简单地将ScrollViewer内部包裹在内部<Border>并设置背景<Border>.如果没有,你将不得不移到所需的背景到主提供的镀铬ControlTemplate的ComboBox.