1

I have three textblocks whose text are bound to three different properties.

<TextBlock Style="{StaticResource textBlockStyle2}"
           Text="{Binding Path=TWD,
                          Mode=TwoWay,
                          UpdateSourceTrigger=PropertyChanged},
                          StringFormat={}{0:F1} M}" />
<TextBlock Style="{StaticResource textBlockStyle2}"
           Text="{Binding Path=Alt,
                          Mode=TwoWay,
                          UpdateSourceTrigger=PropertyChanged},
                          StringFormat={}{0:F1} M}" />
<TextBlock Style="{StaticResource textBlockStyle2}"
           Text="{Binding Path=Dep,
                          Mode=TwoWay,
                          UpdateSourceTrigger=PropertyChanged},
                          StringFormat={}{0:F1} M}" />

These are the properties in the viewmodel:

private double _TWD;
public double TWD
{
    get { return _TWD; }
    set { _TWD = value; OnPropertyChanged("TWD"); }
}

private double _Alt;
public double Alt
{
    get { return _Alt; }
    set { _Alt = value; OnPropertyChanged("Alt"); }
}

private double _Dep;
public double Dep
{
    get { return _Dep; }
    set { _Dep = value; OnPropertyChanged("Dep"); }
}

Now, these are in meters, which is what the 'M' is for in the StringFormat property of the textblocks. What I'm trying to do is, when I 'click' (via command) on a separate textblock (this will be inside of a button using a control template), I want to convert the values in the above textblocks to feet and add an 'F' after the value. Another click will convert it back to meters, and so on.

I was thinking about adding a command that just converted the values based on a bool isMeters. However, the values in the textblock are constantly updated (every second) and I don't want to have to call the function every time the values change. Is there a simpler way I'm not thinking of to achieve this functionality?

Note:

1 meter = 3.2808 ft
1ft = 0.3048 meter
pfinferno
  • 1,779
  • 3
  • 34
  • 62

2 Answers2

2

You could use a <MultiBinding> with an IMultiValueConverter for each property in every TextBlock, but in my opinion this would just mess up the whole XAML, so I really wouldn't recommend it.

But a definitely more viable way could be to have pairs of double and string properties. The former is the raw value which could always be in meters. The latter would represent the current unit that should be displayed in the View.

So your ViewModel would look something like this:

// This is set by the command.
private bool _isMeters = true;

private double _Alt;
public double Alt
{
    get { return _Alt; }
    set { _Alt = value; OnPropertyChanged("Alt"); OnPropertyChanged("AltInCurrentUnit"); }
}
// Rename the suffix as you wish.
public string AltInCurrentUnit => GetInCurrentUnit(_Alt);

// This method is used by all "InCurrentUnit"-properties.
private string GetInCurrentUnit(double meters) =>
    // If you don't like expression bodied methods or ternaries then reformat as you wish.
    _isMeters ?
        $"{meters:F1} M" :
        $"{(meters * 3.2808):F1} F";

And your View just needs to be:

<TextBlock Style="{StaticResource textBlockStyle2}" Text="{Binding AltInCurrentUnit}" />

If your properties are refreshed every second anyway then maybe you don't need any kind of special PropertyChanged calls if the unit is changed by the command.
And of course you can now omit the OnPropertyChanged("Alt") if you don't need to bind this raw property in the View any longer.

haindl
  • 3,111
  • 2
  • 25
  • 31
0

I got it working using a converter.

public class MetersToFeetConverter : IValueConverter
{
    /// <summary>
    /// Converts meters to feet.
    /// </summary>
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        value = (double)value * 3.2808;
        return (value.ToString() + " F"); 
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return Binding.DoNothing;
    }
}

Then in the viewmodel:

private bool _isMeters = true;
public bool IsMeters
{
    get { return _isMeters; }
    set { _isMeters = value; OnPropertyChanged("IsMeters"); }
}

//called when I click the button to convert
public void ConvertData(object parameter)
{
    if (_isMeters == false)
    {
        IsMeters = true;
    }
    else
    {
        IsMeters = false;
    }
}

Then the xaml binding for the textboxes are like this, using a datatrigger:

<Style.Triggers>
    <DataTrigger Binding="{Binding IsMeters, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Value="False">
        <Setter Property="Text" Value="{Binding Path=TWD, Converter={StaticResource metersToFeetConverter}}" />
    </DataTrigger>
</Style.Triggers>

Note: @haindl answer also worked, just didn't see it till after I did it this way.

pfinferno
  • 1,779
  • 3
  • 34
  • 62