1

In a MVVM model, my view uses a value converter. This value converter has a property X that influences the converted value. This property X might change

Whenever X changes, all values that were converted using this value converter need to be updated. Of course My ViewModel does not know that my Views use this converter, so It can't notify PropertyChanged. Besides I think it is not neat to let the ViewModel know in what format values are converted.

My value converter does not know for which values it is used. Luckily my XAML and its code behind class do.

So, my view has two converters as resources, and two text blocks that use these resources:

MyView.XAML:

<UserControl.Resources>
    <convert:FormattedStringConverter x:Key="SelectedHistoryConverter" />
    <vm:TimeFrameConverter x:Key="TimeFrameConverter"/>
</UserControl.Resources>

...

<TextBlock Height="20" Name="HistoryTime"
           Text="{vm:CultureInfoBinding Path=SelectedHistoryTime,
           Converter= {StaticResource SelectedHistoryConverter},
           ConverterParameter='History Time: {0:G}'}"/>

<TextBlock Height="20" Name="Timeframe"
           Text="{vm:CultureInfoBinding Path=Timeframe,
           Converter= {StaticResource TimeFrameConverter},
           ConverterParameter='Time Frame: [{0:G}, {1:G}]'}"/>

Event handler in MyView.XAML.cs

private void OnMyParameterChanged(object sender, EventArgs e)
{
    UpdateConverterParameters(); // updates the changed property in the converters
    UpdateTargets();             // forces an Update of all Targets that use these converters
}

In UpdateTargets I need to tell the two TextBlocks to update their values, which will use the changed converters.

For this I used the accepted answer in StackOverflow: How to force a WPF binding to refresh?

public void UpdateTargets()
{
    BindingExpression historyTimeExpression = HistoryTime.GetBindingExpression(TextBlock.TextProperty);
    historyTimeExpression.UpdateTarget();

    BindingExpression timeframeExpression = Timeframe.GetBindingExpression(TextBlock.TextProperty);
    timeframeExpression .UpdateTarget();
}

This works fine. However this means that whenever I add an element in XAML that uses this binding I'll have to add this element to UpdateTargets.

Is there a way for a class Derived from Binding to know which Targets are bound to it?

Harald Coppoolse
  • 28,834
  • 7
  • 67
  • 116
  • 3
    Instead of a converter with an X property, use MultiBindings that have a Binding to the source of X in your view model. – Clemens Feb 27 '20 at 08:52
  • Binding to X would mean changing my Converters into MultiConverters, with one of the values having the value of X. This works, I tried it, but I thought it would make my converter work by magic: values[0] and values[1] are values to convert; value[2] = x, don't use. If XAML changes order, then my code wouldn't work anymore. Difficult for others to spot the error. A Binding object knows its target and the Converter used for this target, therefore I thought it would be better to let the Binding object notify its target that it must be updated – Harald Coppoolse Feb 27 '20 at 09:52
  • When you pass a Property to the Converter `{Binding MyProperty, Converter=MyValueConverter}`, the control update its depency when the binded property raise its `PropertyChanged`. Simply as it would be without a converter. If you use `IMultiValueConverter`, it will update on each `PropertyChanged` of passed bindings to the converter. (according to your concern, converter receive `object[] values` in the same order as you passed to) So, you may try multi value converter or multi bindings. – aepot Feb 27 '20 at 10:10

2 Answers2

1

Whenever X changes, all values that were converted using this value converter need to be updated.

In this situation, instead of a converter, perhaps have the VM provide the computed values via on demand properties which the controls will subsequently bind to.


So to somewhat borrow from your example I have three properties, two are computed (which do the job of the converter(s)) and one is the existing one which the computed properties utilize.

Computed

public string HistoryTime { get { return SelectedHistoryTime.AddDays(-2).ToShortDate(); } }
public string Timeframe { 
      get 
      {
         return $"Time Frame: [{SelectedHistoryTime.AddDays(-14)}, {SelectedHistoryTime.AddDays(+14).ToShortDate()}]"; 
      }
 }

Existing Notifies All

Then whenever all corresponding properties which HistoryTime and TimeFrame use, in their setters notify a change such as

public DateTime SelectedHistoryTime 
{ 
    get { return _SelectedHistoryTime; }
    set
       {
         _SelectedHistoryTime = value;
         NotifyChange("SelectedHistoryTime");

         // These rely on this property, so they change too
         NotifyChange("HistoryTime");
         NotifyChange("Timeframe");
       }
}
ΩmegaMan
  • 29,542
  • 12
  • 100
  • 122
1

Within the WPF binding system, it is best to allow the system to respond to notifications directly associated with bound values (properties). It is possible to explicitly refresh bindings, as you've found. But that's an error-prone, inefficient way to approach the problem.

As explained here, the best way to do this is to write an IMultiValueConverter instead. This allows for the source properties to be each be bound via INotifyPropertyChanged events, working smoothly with the binding system just like any other properties you might bind. This way, both the main source property and the "property X" will automatically cause the converter to be invoked, updating the target property as needed if either changes.

It is also possible to write a composite property in your view model, as suggested by the other answer. But it's my preference to declare as much of the view-oriented code as possible, rather than putting imperative implementation in the code-behind. This provides the best flexibility, as well as follows the "separation of concerns" philosophy better (i.e. by isolating the "data conversion" aspect in the converter itself, rather than overloading the view model with that logic).

There are numerous examples of how to implement IMultiValueConverter, here on Stack Overflow, on Microsoft's documentation web site, and of course various tutorials on the web. I trust you'll be able to easily apply what you already know about XAML binding syntax, value converters, etc. to implement your current scenario using IMultiValueConverter.

Peter Duniho
  • 68,759
  • 7
  • 102
  • 136