How to perform a binding inside the calling of a converter in WPF XAML?

79 Views Asked by At

This should be very simple, but I can't find it. Currently, I have the following trigger:

<DataTrigger 
  Binding="{Binding warmed,
            Converter={converters:IsGreaterThan 185}}"
  Value="False">

As you can see, there is a comparison to a fix value (185), but I'd like this to be variable, so I tried something like this:

<DataTrigger
  Binding="{Binding warmed,
           Converter={converters:IsGreaterThan 
                      "{Binding MaxMinutes}"
                     }
           }"
  Value="False">

(I've added some whitespace for readability reasons.

Although this should be extremely simple, I don't seem to find it.

I tried to convert my situation in a situation, using such a ConverterParameter, but no success:

<DataTrigger Binding="{Binding warmed, 
                       Converter={converters:IsGreaterThan}, 
                       ConverterParameter=185}"
             Value="False">

This seems not to work. How can I convert my situation in one, using a ConverterParameter?

When opening the "Create Data Binding" dialog box, this is what I see:

enter image description here

As you can see, no trace of that value 185, although it is crucial in my solution. When I fill it in as the ConverterParameter, this is what I get:

<DataTrigger
    Binding="{Binding warmed,
            ConverterParameter=185,
            Converter={converters:IsGreaterThan 185}}"
    Value="False">

... and again no way to declare any multibinding (as I couldn't get it done by trying to type it, I tried to get it using the Visual Studio "Properties" editor).

Edit: a new attempt

Again a try, this time trying to introduce the MultiBinding:

<DataTrigger>
    <DataTrigger.Binding>
        <MultiBinding>
            <Binding Path="warmed"/>
            <Binding Converter="{converters:IsGreaterThan 185}"/>
            <Binding Value="False"/>
        </MultiBinding>
    </DataTrigger.Binding>

It yields two error messages:

XLS0413 : The property 'Value' was not found in type 'Binding'.
XDG0012 : The member "Value" is not recognised or is not accessible.

2

There are 2 best solutions below

0
Emperor Eto On

The comments are correct that it's not possible to directly use a ConverterParameter with a variable using pure XAML. XAML is just inflexible that way.

While a MultiBinding could be made to work here, I think it's a risky approach (more on that later) and certainly overkill. Instead I would simply handle this at the view model level by defining a bool POCO property WarmEnough whose value is derived from Warmed and MaxMinutes:

public class ViewModel : INotifyPropertyChanged
{
    public bool WarmEnough => this.Warmed >= this.MaxMinutes;

    private double _warmed;
    public double Warmed
    {
        get
        {
            return _warmed;
        }
        set
        {
            if (_warmed == value)
                return;
            _warmed = value;
            OnPropertyChanged();
            OnPropertyChanged(nameof(WarmEnough));
        }
    }

    private double _MaxMinutes;
    public double MaxMinutes
    {
        get
        {
            return _MaxMinutes;
        }
        set
        {
            if (_MaxMinutes == value)
                return;
            _MaxMinutes = value;
            OnPropertyChanged();
            OnPropertyChanged(nameof(WarmEnough));
        }
    }

    public event PropertyChangedEventHandler? PropertyChanged;

    private void OnPropertyChanged([CallerMemberName] string? name = null)
    {
        this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
    }
}

Then you simply bind your trigger to the WarmEnough property without regard to converters and the like. (Just make sure you don't need a two-way binding for some reason!) This works because a property change is raised for WarmEnough whenever Warmed or MaxMinutes are changed. So the Trigger will be, er, triggered, whenever either of those properties is changed.

Functionally it's the same as a MultiBinding - inside the Convert method of the IMultiValueConverter you'd basically do the same thing I'm suggesting you do in the getter of WarmEnough - but the problem is the values will come into the multi-converter as an array of objects, whose types you'll need to check and cast, and which will depend on the order of the Bindings in the XAML. This could easily trip up future you and/or your successors without explicit documentation. IMO MultiBinding is really best suited for situations when all the values are the same type and the correct order is either obvious or irrelevant, such as doing boolean AND/OR/XOR checks on multiple conditions, concatenating strings together, etc. Of course it could be done this way so perhaps Clemens will be kind enough to show us. But I would stick with the view model based approach and short circuit all of this.

0
BionicCode On

Valueis a property of the DataTrigger and not Binding. To fix your XAML just set the property on the correct object (DataTrigger). Next, feed in all the data that is required to return true or false i.e. to create the condition using the MultiBinding.

You didn't post any useful information only snippets without context so you have to figure out the details yourself:

TemperatureLimitConverter.cs

class TemperatureLimitConverter : IMultiValueConverter
{
  public object Convert (object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
  {
    double maxMinutes = values.OfType<double>().First();
    bool temperature = values.OfType<double>().Last();

    // Return the boolean that will be evaluated by the DataTrigger
    return temperature > maxMinutes;
  }
    
  public object[] ConvertBack (object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
    => throw new NotSupportedException();
}
<Window.Resources>
  <TemperatureLimitConverter x:Key="TemperatureLimitConverter" />
</Window.Resources>

<DataTrigger Value="False">
  <DataTrigger.Binding>
    <MultiBinding Converter="{StaticResource TemperatureLimitConverter}">
      <Binding Path="warmed" /> 
      <Binding Path="MaxMinutes" />
    </MultiBinding>
  </DataTrigger.Binding>
<DataTrigger Value="False">