绑定作为资源

23
我可以将一个 Binding 定义为 Resource ,然后将其用于不同的 Controls 属性中吗?
示例: 绑定:
<Window.Resources>        
    <Binding x:Key="MyBinding" Path="MyProperty" Mode="TwoWay" />
</Window.Resources>

XAML 中的重用:

<TextBox Text="{StaticResource MyBinding}" />

在以上声明Binding后,我收到了以下错误信息:

"The name 'InitializeComponent' does not exist in the current context"

是否有办法在不同的上下文中重复使用相同的Binding


请参见此处:http://stackoverflow.com/q/9227081/1136211 - Clemens
@Clemens 给出了一些方法,但对我没有用处。我无法得出任何重要信息。 - Kylo Ren
你只能将绑定应用于 DO 的 DP。 - AnjumSKhan
@AnjumSKhan 我知道那个,但那不是我的问题。 - Kylo Ren
4
在技术上很有趣,但重复使用绑定作为资源有什么意义(用例)?难道属性名本身不足以引用源属性吗?此外,您正在预定义绑定模式,这可能完全不适合绑定目标。 - Wouter
2
@Wouter 当需要为ItemsControl创建大量DataTemplate时,这是一种受益。而且你知道每个模板本质上都将绑定到相同的属性。因此,如果我们可以将Binding作为资源实现一定程度的可重用性。请参见此链接:https://dev59.com/aZbfa4cB1Zd3GeqPqjgw#36996526 - Kylo Ren
2个回答

19

直接回答您的问题是,“是的,您可以将绑定定义为资源”。问题在于,您如何使用它?一种可能性是创建一个扩展类,该类会从资源中提取绑定并应用它:

public class BindingResourceExtension : StaticResourceExtension
{
    public BindingResourceExtension() : base() { }

    public BindingResourceExtension(object resourceKey) : base(resourceKey) { }

    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        var binding = base.ProvideValue(serviceProvider) as BindingBase;
        if (binding != null)
            return binding.ProvideValue(serviceProvider);
        else
            return null; //or throw an exception
    }
}

使用示例:

<Window.Resources>
    <ResourceDictionary>
        <Binding x:Key="MyBinding" Path="MyProperty" Mode="TwoWay" />
    </ResourceDictionary>
</Window.Resources>

(...)

<TextBox Text="{ns:BindingResource MyBinding}" />

这个解决方案能在MultiBinding中使用吗?

可以使用:

<TextBlock>
    <TextBlock.Text>
        <MultiBinding StringFormat="First: {0}, Second: {1}">
            <Binding Path="SomeProperty" />
            <ns:BindingResource ResourceKey="MyBinding" />
        </MultiBinding>
    </TextBlock.Text>
</TextBlock>

然而,这种方法有一个缺点 - 尽管一切在运行时都能正常工作,但XAML设计师会抱怨BindingResourceExtension不是适合放在MultiBinding.Bindings集合中的正确类型。 不过,幸运的是,有一个快速解决方案 - 只需使用StaticResourceExtension即可! 因此,虽然它在运行时具有相同的功能,但它将被设计师接受:

<TextBlock>
    <TextBlock.Text>
        <MultiBinding StringFormat="First: {0}, Second: {1}">
            <Binding Path="SomeProperty" />
            <StaticResource ResourceKey="MyBinding" />
        </MultiBinding>
    </TextBlock.Text>
</TextBlock>

1
在XAML中使用扩展类时,可以省略“Extension”后缀 - 这是XAML解析器的一个特性。类似的原则也适用于属性类,可以省略“Attribute”后缀。当然,只要不会引入任何歧义。请注意我们从中派生的StaticResourceExtension类 - 它是在XAML中使用{StaticResource (...)}的同一类。 - Grx70
好的.. 为什么我们必须将这个绑定放在ResourceDictionary中?如果我只是把绑定放在Window.Resource中,它就不起作用,很奇怪。 - Kylo Ren
1
这是因为 XAML 解析器误解了您的意图 - 它被设计成“认为”您尝试使用指定的绑定来绑定 Window.Resources 属性,而不是将绑定添加到字典中。 - Grx70
1
这个能用在多绑定中吗?我无法弄清语法。(我想要重复使用多绑定的其中一个内部绑定) - Romain Hautefeuille
1
在使用VS2015时出现了错误:“无法在类型为'DictionaryEntry'的'Value'属性上设置'Binding'。'Binding'只能在DependencyObject的DependencyProperty上设置”。 - Val
显示剩余2条评论

4
以下是两种不实现您想要功能的方法: 1. 使用自定义标记扩展 为保持简短,省略了所有空值检查等。
using System;
using System.Windows;
using System.Windows.Data;
using System.Windows.Markup;

public class BindingDefinition
{
    public PropertyPath Path { get; set; }

    public BindingMode Mode { get; set; }
}

[MarkupExtensionReturnType(typeof(BindingExpression))]
public class ApplyBindingDefinition : MarkupExtension
{
    public BindingDefinition Definition { get; set; }

    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        var binding = new Binding
        {
            Path = this.Definition.Path,
            Mode = this.Definition.Mode
        };
        return binding.ProvideValue(serviceProvider);
    }
}

<Window.Resources>
    <local:BindingDefinition x:Key="MyProperty"
                             Mode="TwoWay"
                             Path="MyProperty" />
</Window.Resources>
<TextBox>
    <TextBox.Text>
        <!--  using element style here as the parser chokes on parsing nested markupextensions  -->
        <local:ApplyBindingDefinition Definition="{StaticResource MyProperty}" />
    </TextBox.Text>
</TextBox>

2. 将PropertyPath作为资源使用

这可能或者不可能满足您的需求。

<Window.Resources>
    <PropertyPath x:Key="MyPropertyPath">MyProperty</PropertyPath>
</Window.Resources>
...
<TextBox Text="{Binding Path={StaticResource MyPropertyPath}}" />

我正在寻找类似的解决方案...如果整个绑定都可以实现这一点...那么我想要为不同类型的一些动态生成的控件实现最大的可重用性(对于绑定语法)。 - Kylo Ren
@Kylo 更新了答案。 - Johan Larsson
1
设计师在第二次构建后抱怨,但设计师已经出现了无数问题。 - Johan Larsson
这是一个不错的解决方案,然而我想要避免的代码行仍在那里...如果我可以在资源中定义ApplyBindingDefinition并重复使用它,那么会更好。希望以上讲述有意义,无论如何还是谢谢。 - Kylo Ren

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