10

我正在研究一个自定义项MarkupExtension,其中我需要来自 XAML 的非字符串参数来构造新对象。是否可以在范围内的字段上使用非字符串参数绑定DataContext

换句话说,我怎么能做这样的事情?

<ListBox ItemsSource="{Binding Source={local:MyMarkupExtension {x:Type Button},IncludeMethods={Binding Source=CustomerObject.IsProblematic}}}" />

在哪里IncludeMethods=CustomerObject.IsProblematic给我这个错误:

无法在“TypeDescriptorExtension”类型的“IncludeMethods”属性上设置绑定。只能在 DependencyObject 的 DependencyProperty 上设置“绑定”。

谁能帮我?

谢谢

4

4 回答 4

13

只能在 DependencyObject 的 DependencyProperty 上设置“绑定”——这是真的。问题是MarkupExtension类不是从 派生的DependencyObject,这就是为什么不能在它的属性上设置绑定的原因。

[编辑]

解决方法是使用ValueConverters。另一种解决方法是更改​​ C# 语言以允许多重继承。顺便说一句,在 Silverlight 中MarkupExtension实现了IMarkupExtension接口,所以我尝试在我的自定义扩展中实现它并从中派生它DependecyObject,在那里添加DependencyProperty并设置绑定。它不会崩溃,但绑定实际上是在调用 ProvideValue()之后设置的。因此,即使在 Silverlight 中也没有解决方案(或者很难 - 请参阅Klaus78 的答案中提供的链接)。在 WPF MarkupExtension 中没有实现任何接口,所以你不能绑定到它的属性。

于 2012-04-26T07:26:46.097 回答
0

这个链接是关于

具有可绑定属性的自定义标记扩展

编辑有人让我注意到这仅适用于 Silverlight,因为在 WPF MarkupExtension 中没有实现 IMarkupExtension 接口。(谢谢 EvAlex)

于 2012-04-26T08:00:18.927 回答
0

我找到了解决这个问题的方法。
主要思想是为每个需要绑定的参数定义附加属性。

public class MarkupExtensionWithBindableParam : MarkupExtension
{
    public BindingBase Param1 { get; set; } // its necessary to set parameter type as BindingBase to avoid exception that binding can't be used with non DependencyProperty

    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        IProvideValueTarget target = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget;
        DependencyObject targetObject;
        DependencyProperty targetProperty;
        
        if (target != null && target.TargetObject is DependencyObject && target.TargetProperty is DependencyProperty)
        {
            targetObject = (DependencyObject)target.TargetObject;
            targetProperty = (DependencyProperty)target.TargetProperty;
        }
        else
        {
            return this; // magic
        }

        // Bind the Param1 to attached property Param1BindingSinkProperty 
        BindingOperations.SetBinding(targetObject, MarkupExtensionWithBindableParam.Param1BindingSinkProperty, Param1);

        // Now you can use Param1
        
        // Param1 direct access example:
        object param1Value = targetObject.GetValue(Param1BindingSinkProperty);
        
        // Param1 use in binding example:
        var param1InnerBinding = new Binding() { Source = targetObject, Path = new PropertyPath("(0).SomeInnerProperty", Param1BindingSinkProperty) }); // binding to Param1.SomeInnerProperty
        return param1InnerBinding.ProvideValue(serviceProvider); // return binding to Param1.SomeInnerProperty
    }

    private static DependencyProperty Param1BindingSinkProperty = DependencyProperty.RegisterAttached("Param1BindingSink", typeof(object)// set the desired type of Param1 for at least runtime type safety check
                       , typeof(MarkupExtensionWithBindableParam ), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.Inherits));
}

用法很简单:

<TextBlock Text="{local:MarkupExtensionWithBindableParam Param1={Binding Path='SomePathToParam1'}}"/>
于 2016-06-06T22:37:13.950 回答
0

正如其他人所说,请首先考虑使用ValueConverter。这是操作绑定的正确方法。

但是,如果您仍然想使用 MarkupExtension 绑定到视图模型或数据上下文,那么您可以在标记扩展类中手动创建绑定。这类似于@nicolay.anykienko 采用的方法,但我们不需要创建附加属性。

例如,我创建了一个货币符号标记扩展。默认行为是使用CultureInfo.CurrentCulture但少数视图模型具有自己的 CultureInfo 属性,这些属性与当前文化不同。因此,对于这些视图模型,XAML 需要绑定到此属性。请注意,这可以使用 Converter 轻松完成,但为了举例,这里是标记扩展:

public class CurrencySymbolExtension : MarkupExtension
{
    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        var targetProvider = (IProvideValueTarget)serviceProvider.GetService(typeof(IProvideValueTarget));
        var targetElement = targetProvider.TargetObject as FrameworkElement;
        var targetProperty = targetProvider.TargetProperty as DependencyProperty;

        if (!String.IsNullOrEmpty(CultureBindingPath) &&
            targetElement != null &&
            targetProperty != null)
        {
            // make sure that if the binding context changes then the binding gets updated.
            targetElement.DataContextChanged +=
                (sender, args) => ApplyBinding(targetElement, targetProperty, args.NewValue);

            // apply a binding to the target
            var binding = ApplyBinding(targetElement, targetProperty, targetElement.DataContext);

            // return the initial value of the property
            return binding.ProvideValue(serviceProvider);
        }
        else
        {
            // if no culture binding is provided then use the current culture
            return CultureInfo.CurrentCulture.NumberFormat.CurrencySymbol;
        }
    }

    private Binding ApplyBinding(DependencyObject target, DependencyProperty property, object source)
    {
        BindingOperations.ClearBinding(target, property);

        var binding = new Binding(CultureBindingPath + ".NumberFormat.CurrencySymbol")
        {
            Mode = BindingMode.OneWay,
            Source = source,
            FallbackValue = CultureInfo.CurrentCulture.NumberFormat.CurrencySymbol,
        };

        BindingOperations.SetBinding(target, property, binding);
        return binding;
    }

    public string CultureBindingPath { get; set; }
}

然后按如下方式使用:

<!-- Standard Usage -->
<TextBlock Text="{local:CurrencySymbol}"/>

<!-- With DataContext Binding -->
<TextBlock Text="{local:CurrencySymbol CultureBindingPath=ViewModelCulture}"/>

视图模型上的属性在哪里ViewModelCulture用作绑定的源。

于 2020-05-13T14:14:53.027 回答