Use IValueConverter with DynamicResource?
Asked Answered
D

4

19

Is there a way to define a converter when using the DynamicResource extension? Something in the lines of

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

which unfortunately gives me the following excpetion:

A 'DynamicResourceExtension' cannot be set on the 'Source' property of type 'Binding'. A 'DynamicResourceExtension' can only be set on a DependencyProperty of a DependencyObject.

Designed answered 26/1, 2011 at 13:58 Comment(0)
L
26

I know i am really late to this but what definitely works is using a BindingProxy for the DynamicResource like this

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

Then applying the converter to the proxy

<RowDefinition Height="{Binding Source={StaticResource someHeightProxy}, Path=Data, Converter={StaticResource gridLengthConverter}}" />
Leasia answered 15/6, 2014 at 6:59 Comment(4)
This was an awesome find! Bonus that you can also use it to get to the DataContext as described in the article. Already added to our toolset! :)Lungfish
I was one of the people who originally voted this answer up, but I actually just came up with an even simpler solution, that's based on a similar 'proxy' concept, except the proxy is handled automatically and transparently behind a MarkupExtension. Check out my post on this very topic... #33817011Lungfish
I like that! Note that markup extensions can be tough for XAML to discover unless you move them to a separate project/assembly.Leasia
I've found that the discoverability of the markup extension depends on how the XML namespace is defined: xmlns:my="clr-namespace:MyNamespace1.MyNamespace2" compiles and finds the markup extensions, whereas xmlns:my="clr-namespace:MyNamespace1.MyNamespace2;assembly=MyAssembly" doesn't.Unalloyed
L
4

Try something like that:

Markup extension:

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}}" />
Lightweight answered 26/1, 2011 at 14:14 Comment(5)
I get the following compiler error: Unknown property 'Converter' for type 'MS.Internal.Markup.MarkupExtensionParser+UnknownMarkupExtension' Designed
Good idea, but it doesn't work. There is a mismatch: ProvideValue is called once by the XAML parser and should not convert anything. Instead it should provide the dependency property with something that enable the conversion.Mullen
Why didn't you use @Leasia approach, considering he is refencing your article? Is there some sort of drawback?Tilton
@Dzyann, because I hadn't yet written the article when I posted this answer ;)Lightweight
lol! I totally read wrong the dates. But it is good to know you dont see drawbacks on the other approach. Thank you!Tilton
L
4

@Thomas's post is very close, but as others have pointed out, it only executes at the time the MarkupExtension is executed.

Here's a solution that does true binding, doesn't require 'proxy' objects, and is written just like any other binding, except instead of a source and path, you give it a resource key...

How do you create a DynamicResourceBinding that supports Converters, StringFormat?

Lungfish answered 20/11, 2015 at 0:18 Comment(0)
R
1

I like the answer of mkoertgen.

Here is an adapted example for a IValueConverter proxy in VB.NET that worked for me. My resource "VisibilityConverter" is now included as DynamicResource and forwarded with the ConverterProxy "VisibilityConverterProxy".

Usage:

...
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}}"

Code:

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

End Namespace

Regulator answered 25/3, 2015 at 12:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.