如何使用TemplateBinding继承控件的所有属性

7

我正在尝试制作自己版本的控件,比如一个TextBox,我想要添加一些自定义UI。我想要从这个类中继承,这样用户仍然可以像调用普通文本框一样调用我的特殊文本框。我已经有了以下代码:

// MySpecialTextBox.cs

public class MySpecialTextBox : TextBox
{
    static MySpecialTextBox () { }   

    /* Some special properties */     
}

在XAML中:
<Style TargetType="{x:Type MySpecialTextBox}">        
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type MySpecialTextBox}">
                <StackPanel>
                    <Rectangle Width="100" Height="3" Fill="Pink" /> <!-- My own fancy lay-out -->
                    <TextBox Text="{TemplateBinding Text}"/>         <!-- This text box should 'inherit' ALL the properties from the caller -->
                </StackPanel>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

我的问题是,现在TextBox只获取您在此模板中明确设置的属性,例如,只有Text。我还想绑定到BackgroundForeground和其他可能的属性。显然,我可以这样做:

<ControlTemplate TargetType="{x:Type MySpecialTextBox}">
    <StackPanel>
        <Rectangle Width="100" Height="3" Fill="Pink" />
           <TextBox Text="{TemplateBinding Text}" 
                     Background="{TemplateBinding Background}"
                     Foreground="{TemplateBinding Foreground}"
                     AcceptsReturn="{TemplateBinding AcceptsReturn}"
                     AllowDrop="{TemplateBinding AllowDrop}"
                     <!-- ETCETERA -->
                   />
    </StackPanel>
</ControlTemplate>

列出所有属性,但这样做感觉非常低效。有没有一种方法可以一次绑定整个父级元素?


没有,你必须设置每个要使用的属性。 - Sheridan
真的吗? :-( 好吧,我猜否定的答案也是一个答案。也没有解决办法吗? - Yellow
尽管你创建了自己的TextBox类,但它与普通情况并无不同。你需要重写默认模板,并使用TemplateBinding来获取父级属性的任何值。 - Maximus
2个回答

4

在另一个TextBox的模板中放置另一个TextBox元素不是一个好主意。当你覆盖控件的模板时,应该总是编辑默认模板。

所以,在TextBox的默认模板中,你会找到以下元素:

    <Border x:Name="border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="True">
        <ScrollViewer x:Name="PART_ContentHost" Focusable="False" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden"/>
    </Border>

这个边框负责绘制 TextBox。如果你使用这一部分代替内部的 TextBox,你将拥有所有其他 TextBox 属性 “继承”。 编辑: 完整的模板应该是:
 <Style TargetType="{x:Type my:MySpecialTextBox}">
     <Setter Property="Template">
         <Setter.Value>
             <ControlTemplate TargetType="{x:Type my:MySpecialTextBox}">
                 <StackPanel>
                     <Rectangle Width="100" Height="3" Fill="Pink" />
                     <!-- My own fancy lay-out -->
                     <Border x:Name="border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="True">
                         <ScrollViewer x:Name="PART_ContentHost" Focusable="False" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden"/>
                     </Border>
                 </StackPanel>
             </ControlTemplate>
         </Setter.Value>
     </Setter>
 </Style>

你能否详细说明一下?我不完全明白这段代码在哪里或者可以在哪里进行编辑。因为在文本框样式中,没有名为“Border”的属性可供编辑(只有“BorderThickness”和“BorderBrush”)。 - Yellow
在您的模板中,您应该使用名为“PART_ContentHost”的ScrollViewer而不是另一个TextBox。请参阅我的编辑。 - Novitchi S

3
一个TemplateBinding是标准Binding的一种优化形式,大致相当于使用RelativeSource.TemplatedParent属性设置为Binding。因此,像标准的Binding一样,只能在单个属性上设置。来自MSDN上TemplateBindingExtension Class页面的描述:

您可以在模板中使用TemplateBinding来绑定到应用模板的控件上的

来自MSDN上TemplateBinding Markup Extension页面的描述:

... TemplateBinding只有一个可设置的属性...

这意味着我们不能向它提供多个属性名称,因此它只能用于单个属性。不幸的是,没有方便的SuperTemplateBinding可以影响所有属性,但即使有,我也无法想象出任何简单的方法来映射应绑定到哪些模板属性的属性。因此,答案是“没有捷径从模板化的父控件继承属性值”。

TemplateBindingRelativeSource.TemplatedParent 不同。前者在编译时解析,后者在运行时解析。 - Rohit Vats

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