【问题标题】:Binding as a Resource绑定为资源
【发布时间】:2023-08-16 14:58:02
【问题描述】:

我可以将Binding 定义为Resource,然后将其与不同的Controls 属性一起使用吗?

例子:

绑定:

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

在 XAML 中重用:

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

如上所述声明Binding 后,我得到了错误:

"名称'InitializeComponent'在当前不存在 上下文”

有没有办法在不同的上下文中重用相同的Binding

【问题讨论】:

  • @Clemens 它提供了一些方法,但对我没有用。我无法得出任何重要信息
  • 您只能将绑定应用于 DO 的 DP。
  • 技术上很有趣,但是将绑定作为资源重用有什么意义(用例?)?属性名称本身是否不足以引用源属性?此外,您还预定义了可能根本不适合绑定目标的绑定模式。
  • @Wouter 当必须为 ItemsControl 创建大量 DataTemplate 时,这是一种好处。而且您知道每个模板本质上都会绑定到相同的属性。因此,如果我们可以将 Binding 作为资源来实现,那么就可以实现一定程度的可重用性。看到这个*.com/questions/36716010/…

标签: wpf xaml wpf-controls resourcedictionary


【解决方案1】:

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

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>

【讨论】:

  • 很好的解决方案,我想要完全一样的解决方案......但我仍然不清楚这是如何工作的?类名是 BindingResourceExtension 但在 text 属性中你定义了 BindingResource?
  • 在 XAML 中使用扩展类时,您可以省略“扩展”后缀 - 这是 XAML 解析器的一项功能。类似的原则允许您在使用属性类时省略“属性”后缀。只要不引入任何歧义,这当然是正确的。请注意我们从中派生的 StaticResourceExtension 类 - 它与 XAML 中的 {StaticResource (...)} 使用的类相同。
  • 这是因为 XAML 解析器误解了您的意图 - 它旨在“认为”您正在尝试使用指定的绑定来绑定 Window.Resources 属性,而不是将绑定添加到字典中。跨度>
  • 这可以用于多重绑定吗?我无法弄清楚语法。 (我希望复用多重绑定的内部绑定之一)
  • VS2015 出现错误A 'Binding' cannot be set on the 'Value' property of type 'DictionaryEntry'. A 'Binding' can only be set on a DependencyProperty of a DependencyObject
【解决方案2】:

这里有两种方法可以不完全按照您的意愿行事:

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 更新了答案。
  • 设计师在第二次构建后抱怨,但设计师在无数方面都被打破了。
  • 这是一个很好的解决方案,但是我试图避免的代码行仍然存在......如果我可以在资源中定义 ApplyBindingDefinition 然后重用它,那将是更好的解决方案。我希望上面说得通,但无论如何谢谢。