根据命令转换文本框中不断更新的单位

Converting constantly updated units in textboxes on command

我有三个文本块,它们的文本绑定到三个不同的属性。

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

这些是视图模型中的属性:

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 属性 中的用途。我想做的是,当我 'click' (通过命令)在一个单独的文本块上(这将在使用控件模板的按钮内)时,我想将上述文本块中的值转换为英尺并在值后添加 'F'。再次单击会将其转换回米,依此类推。

我正在考虑添加一个命令,该命令仅根据 bool isMeters 转换值。但是,文本块中的值会不断更新(每秒),我不想每次值更改时都必须调用该函数。有没有一种我没有想到的更简单的方法来实现这个功能?

注:

1 meter = 3.2808 ft
1ft = 0.3048 meter

您可以对每个 TextBlock 中的每个 属性 使用 <MultiBinding>IMultiValueConverter,但在我看来,这只会弄乱整个 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")

我使用转换器让它工作。

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

然后在视图模型中:

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 文本框的绑定是这样的,使用数据触发器:

<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 的回答也有效,只是我这样做后才看到它。