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.

bitbonk
  • 48,890
  • 37
  • 186
  • 278

4 Answers4

23

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}}" />
mkoertgen
  • 898
  • 6
  • 14
  • 1
    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! :) – Mark A. Donohoe May 23 '15 at 19:55
  • 2
    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... http://stackoverflow.com/questions/33816511/heres-how-to-create-a-dynamicresourcebinding-that-supports-converters-stringfo – Mark A. Donohoe Nov 20 '15 at 00:19
  • 1
    I like that! Note that markup extensions can be tough for XAML to discover unless you move them to a separate project/assembly. – mkoertgen Nov 22 '15 at 11:25
  • 2
    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. – Zev Spitz Dec 08 '16 at 09:05
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?

Community
  • 1
  • 1
Mark A. Donohoe
  • 28,442
  • 25
  • 137
  • 286
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}}" />
Thomas Levesque
  • 286,951
  • 70
  • 623
  • 758
  • I get the following compiler error: `Unknown property 'Converter' for type 'MS.Internal.Markup.MarkupExtensionParser+UnknownMarkupExtension' ` – bitbonk Apr 14 '11 at 18:13
  • 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. – jeromerg Jun 11 '14 at 11:08
  • Why didn't you use @mkoertgen approach, considering he is refencing your article? Is there some sort of drawback? – Dzyann Sep 24 '15 at 20:33
  • 2
    @Dzyann, because I hadn't yet written the article when I posted this answer ;) – Thomas Levesque Sep 24 '15 at 22:04
  • lol! I totally read wrong the dates. But it is good to know you dont see drawbacks on the other approach. Thank you! – Dzyann Sep 25 '15 at 12:32
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

Stefan
  • 10,010
  • 7
  • 61
  • 117