如果在构造函数中初始化了属性,则似乎未设置XAML绑定 [英] XAML binding doesn't seem to set if the property is initialized in the constructor

查看:71
本文介绍了如果在构造函数中初始化了属性,则似乎未设置XAML绑定的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在构造函数内部初始化属性时,我在控制模板内遇到数据绑定问题。



以下是演示案例(您可以还下载示例解决方案):



CustomControl1.cs

 公共类CustomControl1:ContentControl 
{
静态CustomControl1( )
{
DefaultStyleKeyProperty.OverrideMetadata(
typeof(CustomControl1),
new FrameworkPropertyMetadata(typeof(CustomControl1))));
}

public CustomControl1()
{
Content = Initial; //注释掉这一行,所有
//将开始正常运行
}
}

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>

CustomControl2.cs:

 公共类CustomControl2:ContentControl 
{
静态CustomControl2()
{
DefaultStyleKeyProperty.OverrideMetadata(
typeof(CustomControl2),
。新的FrameworkPropertyMetadata(typeof(CustomControl2)));
}
}

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 = {绑定内容,
RelativeSource = {RelativeSource
AncestorType = local:CustomControl2}} />
< / Border>
< / ControlTemplate>
< /Setter.Value>
< / Setter>
< / Style>

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 =某些内容 />
< / Grid>
< / Window>

因此,问题是:启动应用程序时,内容CustomControl1的值似乎是由构造函数设置的初始,而不是应该由绑定设置的某些内容字符串。





首先,让我预测一下答案:您应该在其元数据中设置依赖项属性的初始值:在注册时或通过元数据覆盖功能。是的,是的,但是这种初始化方法的问题在于该属性属于集合类型,因此如果我提供 new MyCustomCollection()作为默认值,属性,那么 CustomControl1 的每个实例将共享该集合的相同实例,这显然不是主意。



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




  • 创建绑定实例时,将其放入element-像语法一样,并为其分配 x:Name ,然后可以通过 Template.FindName( PART_Binding,this)进行访问在 OnApplyTemplate 内部。

  • 仅在属性上未设置绑定:在同一 OnApplyTemplate 代码 this.GetBindingExpression(ContentProperty)返回 null

  • 绑定本身没有什么问题:在 OnApplyTemplate 中,我们可以查找它,然后进行模拟像下面这样在属性上进行设置: this.SetBinding(ContentProperty,myBinding)-一切正常。



有人可以解释发生这种情况的原因和原因吗?



有人可以解决该问题吗?

预先感谢!



UPD:
最奇怪的是,在两种情况下,具有最高跟踪级别的调试输出是相同的:无论何时不发生绑定或是否绑定



在这里:

  System.Windows.Data警告:52:为绑定(hash = 44419000)创建的BindingExpression(哈希= 18961937)
System.Windows.Data警告:54:路径:内容
System.Windows.Data警告:56:BindingExpression (哈希= 18961937):默认模式已解析为OneWay
System.Windows.Data警告:57:BindingExpression(哈希= 1896193 7):默认更新触发器已解析为PropertyChanged
System.Windows.Data警告:58:BindingExpression(哈希= 18961937):附加到WpfApplication5.CustomControl1.Content(哈希= 47980820)
System.Windows.Data警告:62:BindingExpression(hash = 18961937):RelativeSource(FindAncestor)需要树上下文
System.Windows.Data警告:61:BindingExpression(hash = 18961937):解析源延迟
System.Windows.Data警告:63:BindingExpression(hash = 18961937):解析源
System.Windows.Data警告:66:BindingExpression(hash = 18961937):找到数据上下文元素:< null> (确定)
System.Windows.Data警告:69:类型为CustomControl2的查找祖先:查询边框(hash = 11653293)
System.Windows.Data警告:69:类型为CustomControl2的查找祖先:查询了CustomControl2 (hash = 54636159)
System.Windows.Data警告:68:RelativeSource.FindAncestor找到CustomControl2(hash = 54636159)
System.Windows.Data警告:74:BindingExpression(hash = 18961937):使用根项目CustomControl2(hash = 54636159)
System.Windows.Data警告:104:BindingExpression(hash = 18961937):在级别0-对于CustomControl2.Content找到了访问者DependencyProperty(Content)
System.Windows。数据警告:100:BindingExpression(哈希= 18961937):使用访问器DependencyProperty(Content)
System.Windows.Data警告,将级别0的项替换为CustomControl2(哈希= 54636159),警告:97:BindingExpression(哈希= 18961937) :使用DependencyProperty(Content)从CustomControl2(hash = 54636159)的0级GetValue:'某些内容'
System.Windows.Data警告:76:BindingExpression(哈希= 18961937):TransferValue-获取原始值某些内容
System.Windows.Data警告:85:BindingExpression(哈希值18961937):TransferValue-使用最终值值某些内容

UPD2::向示例解决方案

解决方案

我已经在MSDN论坛上交叉提出了这个问题,有人建议在Microsft Connect上创建一个问题...长话短说:我不清楚的关键机制是 DP的值优先级。在此处(本地值的优先级高于模板化父级的值)。



第二个不太明显的时刻是,如果值由任何模板(甚至不是元素自己的模板)。



HTH。


I've run into a problem with data-binding inside control template while the property is initialized inside the constructor.

Here is the show-case (you can also download sample solution):

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
    }
}

CustomControl1 style:

<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>

CustomControl2.cs:

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

CustomControl style:

<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>

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>

So, the problem is: when you launch the app, the content of CustomControl1 appears to be "Initial" which is set by constructor, not the "Some content" string, which is supposed to be set by binding.

When we remove the initialization from the constructor, the binding starts working.

First of all, let me predict the answer: "you should set the initial value of a dependency property inside its metadata: either at the moment of registration or by means of metadata overriding capabilities". Yeap, you right, but the problem with this method of initialization is that the property is of collection type, so if I'll provide new MyCustomCollection() as a default value of the property, then every instance of CustomControl1 will share the same instance of that collection and that's obviously not the idea.

I've done some debugging on the problem, here are the results:

  • Binding instance is created, when we put it in element-like syntax and assign x:Name to it, then it's accessible through Template.FindName("PART_Binding", this) inside OnApplyTemplate.
  • Binding simply isn't set on the property: inside the same OnApplyTemplate the code this.GetBindingExpression(ContentProperty) return null.
  • There is nothing wrong with the binding itself: inside OnApplyTemplate we can look it up and then we can simply set it on the property like this: this.SetBinding(ContentProperty, myBinding) - everything will work fine.

Can anyone explain how and why that happens?

Does anyone have a solution for setting non-shared initial value for a dependency property, so the binding wouldn't break?

Thanks in advance!

UPD: The most weird thing is that debug output with highest trace-level is the same for both cases: either when the binding doesn't occur or if it does.

Here it goes:

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'

UPD2: added a link to the sample solution

解决方案

I've crossposted the problem at MSDN forums, someone there has suggested to create an issue at Microsft Connect... Long story short: the key mechanism I didn't clearly understand was DP's value precedence. It is perfectly described here (local value has higher priority than templated parent's value).

Second, not really obvious moment is that the value is considered as templated parent's if it was set by any template (not even element's own template).

HTH.

这篇关于如果在构造函数中初始化了属性,则似乎未设置XAML绑定的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆