如果在构造函数中初始化属性,则似乎不会设置XAML绑定

arc*_*592 7 c# data-binding wpf xaml custom-controls

在构造函数中初始化属性时,我遇到了控件模板内部数据绑定的问题.

这是展示案例(您也可以下载示例解决方案):

CustomControl1.cs

public class CustomControl1 : ContentControl
{
    static CustomControl1()
    {
        DefaultStyleKeyProperty.OverrideMetadata(
            typeof(CustomControl1), 
            new FrameworkPropertyMetadata(typeof(CustomControl1)));
    }

    public CustomControl1()
    {
        Content = "Initial"; // comment this line out and everything 
                             // will start working just great
    }
}
Run Code Online (Sandbox Code Playgroud)

CustomControl1风格:

<Style TargetType="{x:Type local:CustomControl1}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:CustomControl1}">
                <Border Background="{TemplateBinding Background}"
                        BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="{TemplateBinding BorderThickness}">
                    <ContentPresenter />
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>
Run Code Online (Sandbox Code Playgroud)

CustomControl2.cs:

public class CustomControl2 : ContentControl
{
    static CustomControl2()
    {
        DefaultStyleKeyProperty.OverrideMetadata(
            typeof(CustomControl2), 
            new FrameworkPropertyMetadata(typeof(CustomControl2)));
    }
}
Run Code Online (Sandbox Code Playgroud)

CustomControl风格:

<Style TargetType="{x:Type local:CustomControl2}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:CustomControl2}">
                <Border Background="{TemplateBinding Background}"
                        BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="{TemplateBinding BorderThickness}">
                    <local:CustomControl1 
                        Content="{Binding Content, 
                            RelativeSource={RelativeSource 
                                    AncestorType=local:CustomControl2}}" />
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>
Run Code Online (Sandbox Code Playgroud)

Window1.xaml:

<Window x:Class="WpfApplication5.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Window1" Height="300" Width="300"
        xmlns:local="clr-namespace:WpfApplication5">
    <Grid>
        <local:CustomControl2 Content="Some content" />
    </Grid>
</Window>
Run Code Online (Sandbox Code Playgroud)

因此,问题是:当您启动应用程序时,CustomControl1的内容似乎是"Initial",它由构造函数设置,而不是"Some content"字符串,应该通过绑定来设置.

当我们从构造函数中删除初始化时,绑定开始工作.

首先,让我预测答案:"你应该在其元数据中设置依赖属性的初始值:在注册时或通过元数据覆盖功能".是的,你是对的,但这种初始化方法的问题是属性是集合类型,所以如果我将提供new MyCustomCollection()属性的默认值,那么每个实例CustomControl1将共享该集合的同一个实例,那就是显然不是这个想法.

我已经对问题做了一些调试,结果如下:

  • 创建绑定实例,当我们将它放入类似元素的语法并分配x:Name给它时,它可以通过Template.FindName("PART_Binding", this)内部访问OnApplyTemplate.
  • 简单的绑定不是在属性上设置的:在OnApplyTemplate代码this.GetBindingExpression(ContentProperty)返回的内部相同null.
  • 绑定本身没有任何问题:在里面OnApplyTemplate我们可以查找它然后我们可以简单地在它上面设置它:this.SetBinding(ContentProperty, myBinding)- 一切都会正常工作.

谁能解释为什么会发生这种情况?

有没有人有一个解决方案来设置依赖属性的非共享初始值,所以绑定不会中断?

提前致谢!

UPD: 最奇怪的是具有最高跟踪级别的调试输出对于两种情况都是相同的:要么在没有发生绑定时,要么就是这样.

在这里:

System.Windows.Data Warning: 52 : Created BindingExpression (hash=18961937) for Binding (hash=44419000)
System.Windows.Data Warning: 54 :   Path: 'Content'
System.Windows.Data Warning: 56 : BindingExpression (hash=18961937): Default mode resolved to OneWay
System.Windows.Data Warning: 57 : BindingExpression (hash=18961937): Default update trigger resolved to PropertyChanged
System.Windows.Data Warning: 58 : BindingExpression (hash=18961937): Attach to WpfApplication5.CustomControl1.Content (hash=47980820)
System.Windows.Data Warning: 62 : BindingExpression (hash=18961937): RelativeSource (FindAncestor) requires tree context
System.Windows.Data Warning: 61 : BindingExpression (hash=18961937): Resolve source deferred
System.Windows.Data Warning: 63 : BindingExpression (hash=18961937): Resolving source 
System.Windows.Data Warning: 66 : BindingExpression (hash=18961937): Found data context element: <null> (OK)
System.Windows.Data Warning: 69 :     Lookup ancestor of type CustomControl2:  queried Border (hash=11653293)
System.Windows.Data Warning: 69 :     Lookup ancestor of type CustomControl2:  queried CustomControl2 (hash=54636159)
System.Windows.Data Warning: 68 :   RelativeSource.FindAncestor found CustomControl2 (hash=54636159)
System.Windows.Data Warning: 74 : BindingExpression (hash=18961937): Activate with root item CustomControl2 (hash=54636159)
System.Windows.Data Warning: 104 : BindingExpression (hash=18961937):   At level 0 - for CustomControl2.Content found accessor DependencyProperty(Content)
System.Windows.Data Warning: 100 : BindingExpression (hash=18961937): Replace item at level 0 with CustomControl2 (hash=54636159), using accessor DependencyProperty(Content)
System.Windows.Data Warning: 97 : BindingExpression (hash=18961937): GetValue at level 0 from CustomControl2 (hash=54636159) using DependencyProperty(Content): 'Some content'
System.Windows.Data Warning: 76 : BindingExpression (hash=18961937): TransferValue - got raw value 'Some content'
System.Windows.Data Warning: 85 : BindingExpression (hash=18961937): TransferValue - using final value 'Some content'
Run Code Online (Sandbox Code Playgroud)

UPD2:添加了示例解决方案的链接

arc*_*592 5

我在 MSDN 论坛上交叉发布了这个问题,那里有人建议在 Microsft Connect 上创建一个问题...长话短说:我没有清楚理解的关键机制是DP 的值优先级这里完美地描述了(本地值比模板化父值的优先级更高)。

其次,不太明显的一点是,如果该值是由任何模板(甚至不是元素自己的模板)设置的,则该值将被视为模板化父级的值。

HTH。