命名空间问题:找不到附加的依赖项属性

时间:2017-01-23 16:52:08

标签: c# wpf xaml

我有一个简单的附加依赖属性,它应该为我的自定义控件提供鼠标颜色:

public class Ext
{
    public static readonly DependencyProperty HighlightBrushProperty = DependencyProperty.RegisterAttached("HighlightBrush", typeof(Brush), typeof(Ext), new PropertyMetadata(default(Brush)));

    public static void SetHighlightBrush(DependencyObject element, Brush value)
    {
        element.SetValue(HighlightBrushProperty, value);
    }

    public static Brush GetHighlightBrush(DependencyObject element)
    {
        return (Brush) element.GetValue(HighlightBrushProperty);
    }
}

我在我的Generic.xaml

中使用它
<Style TargetType="{x:Type local:MyButton}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type Button}">
                <Border x:Name="Border" 
                        Background="{TemplateBinding Background}"
                        BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="{TemplateBinding BorderThickness}">
                    <ContentPresenter />
                </Border>
                <ControlTemplate.Triggers>
                    <Trigger Property="IsMouseOver" Value="True">
                        <Setter TargetName="Border" Property="Background" Value="{Binding (local:Ext.HighlightBrush), RelativeSource={RelativeSource TemplatedParent}}" />
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

但是现在我必须在客户端中使用相同的命名空间:

<Window ...
        xmlns:local="clr-namespace:Eval.Wpf.AttachedProperty.Controls;assembly=Eval.Wpf.AttachedProperty.Controls">
    <StackPanel>
        <local:MyButton Background="LightGray" local:Ext.HighlightBrush="DarkOrange" Content=" - Press me - " />
    </StackPanel>
</Window>

将命名空间更改为其他内容,例如<c:MyButton Background="LightGray" c:Ext.HighlightBrush="DarkOrange" Content=" - Press me - " />最终会在

System.Windows.Data Error: 40 : BindingExpression path error: '(local:Ext.HighlightBrush)' property not found on 'object' ''MyButton' (Name='')'. BindingExpression:Path=(local:Ext.HighlightBrush); DataItem='MyButton' (Name=''); target element is 'Border' (Name='Border'); target property is 'Background' (type 'Brush')

为什么使用附加属性不安全?我该如何解决这个问题?

1 个答案:

答案 0 :(得分:1)

解决方案

正如我的评论所述,您的案例中的解决方案是明确使用Binding.Path属性,即:

{Binding Path=(local:Ext.HighlightBrush), ...}

诊断

我不确定这种行为背后的原因是什么,但我已经做了一些测试,并将分享结果和观察结果。

首先,这是一个说明问题的简明示例(摘自Window XAML 定义):

<Window.Resources>
    <ControlTemplate x:Key="T1" xmlns:foo="clr-namespace:FooBar">
        <TextBlock Text="{Binding (foo:MainWindow.Test),
                                  RelativeSource={RelativeSource TemplatedParent}}" />
    </ControlTemplate>
</Window.Resources>
<Grid xmlns:bar="clr-namespace:FooBar">
    <Control x:Name="C1" bar:MainWindow.Test="test" Template="{StaticResource T1}" />
</Grid>

我们最终遇到绑定错误,并显示以下消息:

System.Windows.Data Error: 40 : BindingExpression path error:
    '(foo:MainWindow.Test)' property not found on 'object' ''Control' (Name='C1')'.
    BindingExpression:Path=(foo:MainWindow.Test);
    DataItem='Control' (Name='C1');
    target element is 'TextBlock' (Name='');
    target property is 'Text' (type 'String')

当然,如果我们使用Binding.Path属性,一切正常。

现在,如果我们在绑定上设置PresentationTraceSource.TraceLevel="High",我们可以看到输出中的(至关重要的)差异 - 在前一种情况下我们得到

System.Windows.Data Warning: 58 :   Path: '(foo:MainWindow.Test)'

但在后一种情况下我们得到

System.Windows.Data Warning: 58 :   Path: '(0)'

所以似乎使用第一种语法相当于用

实例化路径
new PropertyPath("(foo:MainWindow.Test)")

(我认为它是由new Binding("(foo:MainWindow.Test)")构造函数重载完成的),但第二个似乎使用了PathProperty构造函数的另一个重载:

new PropertyPath("(0)", MainWindow.TestProperty)

<强>声明 接下来我要说的只是我的假设,所以它可能只是错误。

看起来"(foo:MainWindow.Test)"路径被视为CLR访问器,只有在有可用的绑定源时才会解析。在这种情况下,源是C1控件。该框架足够聪明,可以知道控件定义的 XAML 的哪个部分,因此它尝试使用该上下文解析附加属性。但在我们的情况下,foo前缀未在该上下文中定义,因此解析失败,因此绑定失败。一个似乎证实该假设的观察是,如果你定义所述前缀,即使在控制本身上:

<Control x:Name="C1" xmlns:foo="clr-namespace:FooBar" (...) />

绑定开始按预期工作。另外,如果我们修改我们的模板:

<ControlTemplate x:Key="T1">
    <TextBlock Text="{Binding (bar:MainWindow.Test),
                              RelativeSource={RelativeSource TemplatedParent}}" />
</ControlTemplate>

即使在此上下文中未定义bar前缀,并且 XAML 设计器抱怨它,它仍然 在运行时工作。

第二种方法的作用是在实例化Binding时解析属性 - 此时必须解析该属性才能传递给PropertyPath构造函数。因为在 XAML Binding的上下文中定义了foo前缀,所以解析成功并且一切正常。

总之,似乎这两种情况的区别仅在于框架尝试将(foo:MainWindow.Test)字符串解析为实际属性的时间,但这不仅仅是差异差异