WPF DataBinding with simple arithmetic operation?
-
02-07-2019 - |
Question
I want to add a constant value onto an incoming bound integer. In fact I have several places where I want to bind to the same source value but add different constants. So the ideal solution would be something like this...
<TextBox Canvas.Top="{Binding ElementName=mySource, Path=myInt, Constant=5}"/>
<TextBox Canvas.Top="{Binding ElementName=mySource, Path=myInt, Constant=8}"/>
<TextBox Canvas.Top="{Binding ElementName=mySource, Path=myInt, Constant=24}"/>
(NOTE: This is an example to show the idea, my actual binding scenario is not to the canvas property of a TextBox. But this shows the idea more clearly)
At the moment the only solution I can think of is to expose many different source properties each of which adds on a different constant to the same internal value. So I could do something like this...
<TextBox Canvas.Top="{Binding ElementName=mySource, Path=myIntPlus5}"/>
<TextBox Canvas.Top="{Binding ElementName=mySource, Path=myIntPlus8}"/>
<TextBox Canvas.Top="{Binding ElementName=mySource, Path=myIntPlus24}"/>
But this is pretty grim because in the future I might need to keep adding new properties for new constants. Also if I need to change the value added I need to go an alter the source object which is pretty naff.
There must be a more generic way than this? Any WPF experts got any ideas?
Solution
I believe you can do this with a value converter. Here is a blog entry that addresses passing a parameter to the value converter in the xaml. And this blog gives some details of implementing a value converter.
OTHER TIPS
I use a MathConverter
that I created to do all simple arithmatic operations with. The code for the converter is here and it can be used like this:
<TextBox Canvas.Top="{Binding SomeValue,
Converter={StaticResource MathConverter},
ConverterParameter=@VALUE+5}" />
You can even use it with more advanced arithmatic operations such as
Width="{Binding ElementName=RootWindow, Path=ActualWidth,
Converter={StaticResource MathConverter},
ConverterParameter=((@VALUE-200)*.3)}"
Using a value converter is a good solution to the problem as it allows you to modify the source value as it's being bound to the UI.
I've used the following in a couple of places.
public class AddValueConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
object result = value;
int parameterValue;
if (value != null && targetType == typeof(Int32) &&
int.TryParse((string)parameter,
NumberStyles.Integer, culture, out parameterValue))
{
result = (int)value + (int)parameterValue;
}
return result;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
Example
<Setter Property="Grid.ColumnSpan"
Value="{Binding
Path=ColumnDefinitions.Count,
RelativeSource={RelativeSource AncestorType=Grid},
Converter={StaticResource addValueConverter},
ConverterParameter=1}"
/>
I've never used WPF, but I have a possible solution.
Can your binding Path map to a Map? If so, it should then be able to take an argument (the key). You'd need to create a class that implements the Map interface, but really just returns the base value that you initialized the "Map" with added to the key.
public Integer get( Integer key ) { return baseInt + key; } // or some such
Without some ability to pass the number from the tag, I don't see how you're going to get it to return different deltas from the original value.