Преобразование постоянно обновляемых единиц в текстовые поля по команде

У меня есть три текстовых блока, текст которых связан с тремя различными свойствами.

<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}" />

Это свойства в 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"); }
}

Теперь они выражены в метрах, для чего и используется "M" в свойстве StringFormat текстовых блоков. То, что я пытаюсь сделать, это когда я "нажимаю" (с помощью команды) на отдельном текстовом блоке (это будет внутри кнопки, использующей шаблон элемента управления), я хочу преобразовать значения в вышеуказанных текстовых блоках в футы и добавить 'F' после значения. Еще один клик преобразует его обратно в метры и так далее.

Я думал о добавлении команды, которая просто преобразовала значения на основе bool isMeters, Тем не менее, значения в текстовом блоке постоянно обновляются (каждую секунду), и я не хочу вызывать функцию каждый раз, когда значения меняются. Есть ли более простой способ, о котором я не думаю, чтобы достичь этой функциональности?

Замечания:

1 meter = 3.2808 ft
1ft = 0.3048 meter

2 ответа

Решение

Я получил это работает с помощью конвертера.

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;
    }
}

Тогда в 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;
    }
}

Тогда привязка xaml к текстовым полям выглядит так, используя 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>

Примечание: ответ @haindl тоже сработал, просто не видел его, пока я так не сделал.

Вы могли бы использовать <MultiBinding> с IMultiValueConverter для каждого свойства в каждом TextBlock, но, на мой взгляд, это просто испортило бы весь XAML, поэтому я действительно не рекомендовал бы это.

Но определенно более жизнеспособным способом могло бы быть наличие пары двойных и строковых свойств. Первая - это необработанная стоимость, которая всегда может быть в метрах. Последний будет представлять текущую единицу, которая должна отображаться в представлении.

Так что ваша ViewModel будет выглядеть примерно так:

// 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";

И ваш взгляд просто должен быть:

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

Если ваши свойства обновляются каждую секунду, то, возможно, вам не нужны какие-либо специальные PropertyChanged звонки, если единица изменена командой.
И, конечно, теперь вы можете опустить OnPropertyChanged("Alt") если вам больше не нужно связывать это необработанное свойство в представлении.

Другие вопросы по тегам