Using Value Converters in WPF without having to define them as resources first

18,719

Solution 1

In the case of singleton-type IValueConverters (e.g. they don't need any state from the current binding instance) I use static converters, i.e.:

Converter={x:Static SomeNamespace:SomeConverter.Instance}

There's also a great post by Dr. WPF on using a markup extension to make it cleaner inline here.

Solution 2

Technically I believe you can do this, but the XAML is so horrible that it will make the "lots of trivial resources" approach look like a haven of simplicity and clarity by comparison:

<Button Height="50">
  <Button.Width>
    <Binding Path="Width" ElementName="textBox1" UpdateSourceTrigger="PropertyChanged">
      <Binding.Converter>
        <local:TrivialFormatter />
      </Binding.Converter>
    </Binding>
  </Button.Width>
</Button>

I have not tested this because even reading it makes my eyes water...

Solution 3

I would definitely look into Micah's suggestion which involves using a static singleton instance of your converter. But another thing to consider is that if you're using a separated presentation pattern like MVVM you can often avoid the requirement for a value converter by implementing the conversion in the ViewModel.

There's a lot of reasons you might want to do this.

For one, it is much more testable. Your unit tests can be sure that whatever is coming out of the ViewModel is what will be displayed by the UI. You can imagine testing a requirement that dollar values must follow the current culture's currency format, two decimals must be used, etc.

Another good reason is that exceptions in value converters will not be treated as validation errors which can be a huge pain in the butt in Silverlight. Even if you set ValidatesOnExceptions to true in the binding, if your value converter throws an exception, Silverlight will just let it propagate. If however you use the ViewModel to do the conversion, an exception will be treated as a validation error.

The downside is that you lose some of the "reusability" of a general purpose value converter.

Solution 4

I don't know of a way to do this the way your are stating, but I just tried this as a sample and it worked. In your App.xaml.cs file you can create a method that uses reflection to load the converters.

private void Application_Startup(object sender, StartupEventArgs e)
{
    LoadConverters();
}

private void LoadConverters()
{
    foreach(var t in Assembly.GetExecutingAssembly().GetTypes())
    {
        if (t.GetInterfaces().Any(i => i.Name == "IValueConverter"))
        {
            Resources.Add(t.Name, Activator.CreateInstance(t));
        }
    }
}

Then you can use the converter with like this, half way there I guess.

<Button Width="{Binding Width, Converter={StaticResource TrivialFormatter}}" />

The problem with the approach you are proposing is that the Xaml parser doesn't know when and how many instances of your converter to create. Creating it as a resource ensure only one instance.

Share:
18,719

Related videos on Youtube

devoured elysium
Author by

devoured elysium

Updated on October 27, 2020

Comments

  • devoured elysium
    devoured elysium over 3 years

    Is it possible to use value converters without having to define them beforehand as resources?

    Right now I have

    <Window.Resources>
        <local:TrivialFormatter x:Key="trivialFormatter" />
    </Window.Resources>
    

    and

    <Button Width="{Binding Width, 
                   ElementName=textBox1, 
                   UpdateSourceTrigger=PropertyChanged, 
                   Converter={StaticResource trivialFormatter}}">
    

    Wouldn't it be possible that instead of having to declare the trivialFormatter resource in Window.Resources, I could directly refer it from the Button's width binding? Something like

    Converter = {local:TrivialFormatter}
    

    Thanks

  • user6170001
    user6170001 about 14 years
    On a similar theme, you could create a markup extension which returns an instance of its argument type. Then you could write Converter={local:InstanceOf {x:Type local:TrivialFormatter}}. As you note, though, this would result in a separate instantiation for every binding, which is potentially wasteful (especially if used in the ItemTemplate for a large ItemsControl).
  • user6170001
    user6170001 about 14 years
    Nice trick with the markup extension! On your first example, though, don't you need to make a static instance of the VC, e.g. {x:Static local:SomeConverter.Instance}?
  • Josh
    Josh about 14 years
    This is a very good suggestion. When creating your own value converters you should always prefer to have them accept a ConverterParameter instead of having instance properties so that you can use a singleton instance like this. It's SO much easier than using resources. I usually call mine something like {x:Static Local:InvertedBooleanConverter.Default} or something.
  • Josh
    Josh about 14 years
    This is a perfectly valid way of doing it too. But as you noted the verbosity of XAML is like the Ark of the Covenant and merely looking at it will devour your soul.
  • naveen jayanna
    naveen jayanna about 14 years
    @ itowlson -- yes on the instance - I've edited my response. Thx for the catch.
  • chviLadislav
    chviLadislav over 6 years
    This approach might be useful in case you want to set some property on a specific instance of the converter, like <local:TrivialFormatter RoundToTenth="True"/>

Related