使用DynamicResource和IValueConverter?

19

在使用DynamicResource扩展时是否有定义转换器的方法?类似以下的方式:

<RowDefinition Height="{Binding Source={DynamicResource someHeight}, Converter={StaticResource gridLengthConverter}}" />

不幸的是,它给了我以下异常:

无法在类型为“Binding”的“Source”属性上设置'DynamicResourceExtension'。只能在DependencyObject的DependencyProperty上设置“DynamicResourceExtension”。

4个回答

24

我知道我来晚了,但肯定有效的方法是像这样使用BindingProxy来进行DynamicResource绑定

<my:BindingProxy x:Key="someHeightProxy" Data="{DynamicResource someHeight}" />

然后将转换器应用于代理

<RowDefinition Height="{Binding Source={StaticResource someHeightProxy}, Path=Data, Converter={StaticResource gridLengthConverter}}" />

1
这是一个很棒的发现!而且正如文章中所描述的那样,你还可以使用它来访问 DataContext。已经被添加到我们的工具集中了! :) - Mark A. Donohoe
2
我是最初投票支持这个答案的人之一,但我实际上想出了一个更简单的解决方案,它基于类似“代理”的概念,只不过代理是通过MarkupExtension自动透明地处理的。请查看我在这个问题上的帖子...https://dev59.com/elsX5IYBdhLWcg3wcPEF - Mark A. Donohoe
1
我喜欢这个!请注意,除非将标记扩展移动到单独的项目/程序集中,否则 XAML 可能很难发现它们。 - mkoertgen
2
我发现标记扩展的可发现性取决于XML命名空间的定义方式:xmlns:my="clr-namespace:MyNamespace1.MyNamespace2" 可以编译并找到标记扩展,而 xmlns:my="clr-namespace:MyNamespace1.MyNamespace2;assembly=MyAssembly" 则不行。 - Zev Spitz

4

请尝试类似于这样的操作:

标记扩展:

public class DynamicResourceWithConverterExtension : DynamicResourceExtension
{
    public DynamicResourceWithConverterExtension()
    {
    }

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

    public IValueConverter Converter { get; set; }
    public object ConverterParameter { get; set; }

    public override object ProvideValue(IServiceProvider provider)
    {
        object value = base.ProvideValue(provider);
        if (value != this && Converter != null)
        {
            Type targetType = null;
            var target = (IProvideValueTarget)provider.GetService(typeof(IProvideValueTarget));
            if (target != null)
            {
                DependencyProperty targetDp = target.TargetProperty as DependencyProperty;
                if (targetDp != null)
                {
                    targetType = targetDp.PropertyType;
                }
            }
            if (targetType != null)
                return Converter.Convert(value, targetType, ConverterParameter, CultureInfo.CurrentCulture);
        }

        return value;
    }
}

XAML:

<RowDefinition Height="{my:DynamicResourceWithConverter someHeight, Converter={StaticResource gridLengthConverter}}" />

我得到了以下编译器错误:类型 'MS.Internal.Markup.MarkupExtensionParser+UnknownMarkupExtension' 的属性 'Converter' 未知。 - bitbonk
好主意,但它不起作用。存在不匹配:ProvideValue 被 XAML 解析器仅调用一次,并且不应该进行任何转换。相反,它应该提供某些使转换变得可行的依赖属性。 - jeromerg
你为什么没有采用@mkoertgen的方法,考虑到他在引用你的文章?有什么缺点吗? - Dzyann
2
@Dzyann,因为我在发布这个答案时还没有写文章 ;) - Thomas Levesque
哈哈!我完全读错了日期。但是很高兴知道你没有看到另一种方法的缺点。谢谢! - Dzyann

4

@Thomas的帖子非常接近,但正如其他人所指出的那样,它仅在执行MarkupExtension时执行。

这里有一个解决方案,可以进行真正的绑定,不需要“代理”对象,并且编写方式与任何其他绑定相同,只是您不是使用源和路径,而是提供资源键...

如何创建支持转换器、StringFormat的DynamicResourceBinding?


1

我喜欢mkoertgen的回答。

这是一个适用于VB.NET的IValueConverter代理的示例,对我很有效。我的资源“VisibilityConverter”现在作为DynamicResource包含,并通过“VisibilityConverterProxy”转发。

用法:

...
xmlns:binding="clr-namespace:Common.Utilities.ModelViewViewModelInfrastructure.Binding;assembly=Common"
...
<ResourceDictionary>
    <binding:ConverterProxy x:Key="VisibilityConverterProxy" Data="{DynamicResource VisibilityConverter}" />
</ResourceDictionary>
...
Visibility="{Binding IsReadOnly, Converter={StaticResource VisibilityConverterProxy}}"

代码:

Imports System.Globalization

Namespace Utilities.ModelViewViewModelInfrastructure.Binding

''' <summary>
''' The ConverterProxy can be used to replace StaticResources with DynamicResources.
''' The replacement helps to test the xaml classes. See ToolView.xaml for an example
''' how to use this class.
''' </summary>
Public Class ConverterProxy
    Inherits Freezable
    Implements IValueConverter

#Region "ATTRIBUTES"

    'Using a DependencyProperty as the backing store for Data.  This enables animation, styling, binding, etc...
    Public Shared ReadOnly DataProperty As DependencyProperty =
                                DependencyProperty.Register("Data", GetType(IValueConverter), GetType(ConverterProxy), New UIPropertyMetadata(Nothing))

    ''' <summary>
    ''' The IValueConverter the proxy redirects to
    ''' </summary>
    Public Property Data As IValueConverter
        Get
            Return CType(GetValue(DataProperty), IValueConverter)
        End Get
        Set(value As IValueConverter)
            SetValue(DataProperty, value)
        End Set
    End Property

#End Region


#Region "METHODS"

    Protected Overrides Function CreateInstanceCore() As Freezable
        Return New ConverterProxy()
    End Function

    Public Function Convert(ByVal value As Object, ByVal targetType As Type, ByVal parameter As Object, ByVal culture As CultureInfo) As Object Implements IValueConverter.Convert
        Return Data.Convert(value, targetType, parameter, culture)
    End Function

    Public Function ConvertBack(ByVal value As Object, ByVal targetType As Type, ByVal parameter As Object, ByVal culture As CultureInfo) As Object Implements IValueConverter.ConvertBack
        Return Data.ConvertBack(value, targetType, parameter, culture)
    End Function

#End Region



End Class

结束命名空间


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