如何创建有条件地转换用户输入的WPF文本框?

如何创建有条件地转换用户输入的WPF文本框?,wpf,textbox,imultivalueconverter,Wpf,Textbox,Imultivalueconverter,我想创建一个文本框,可以进行测量,并在必要时将其转换为不同的单位(最终结果为类型double)。转换将由值IsMetric控制。如果IsMetric==true,则“中的36.5将变成927.1(一个双精度表示毫米)。相反,如果IsMetric==false,则“927.1 mm”将变为36.5 我想在常规的文本框上使用IValueConverter,但是ConverterParameter不是依赖属性,因此我无法绑定IsMetric 我尝试了IMultiValueConverter,但是Con

我想创建一个文本框,可以进行测量,并在必要时将其转换为不同的单位(最终结果为类型
double
)。转换将由值
IsMetric
控制。如果
IsMetric==true
,则“中的
36.5将变成
927.1
(一个双精度表示毫米)。相反,如果
IsMetric==false
,则
“927.1 mm”
将变为
36.5

我想在常规的
文本框上使用
IValueConverter
,但是
ConverterParameter
不是
依赖属性,因此我无法绑定
IsMetric

我尝试了
IMultiValueConverter
,但是
ConvertBack
函数只接收
文本框的当前值,而不是所有绑定值。这意味着在转换用户输入时,我不知道
IsMetric


我是否错过了
ConvertBack
功能?如果没有,那么我是否需要创建一个从
文本框
派生的类?

如果这是您唯一想做的事情,请尝试使用converter参数的其他方法。
但是,我会选择这个选项——如果您的textbox中有更多的逻辑,或者倾向于有更多的dependecie——创建从textbox继承的自定义控件,并添加您自己的dependeci属性。然后,您可以使用IsMetric并根据需要在propertychanged等上对其进行转换。

您可以使用两个转换器,一个用于从公制转换,另一个用于转换为公制:

public class ToMetricConverter:IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return "(metric) value";
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}
public class FromMetricConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return "(Inch) value";
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}
并在UI中使用
DataTrigger
,根据该布尔值选择适当的转换器:

<Window.Resources>
    <wpfApplication13:ToMetricConverter x:Key="ToMetricConverter"/>
    <wpfApplication13:FromMetricConverter x:Key="FromMetricConverter"/>
</Window.Resources>
<Grid>
    <StackPanel>    
        <CheckBox IsChecked="{Binding IsMetric,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"></CheckBox>
        <TextBox >
            <TextBox.Style>
                <Style TargetType="TextBox">                        
                    <Style.Triggers>
                        <DataTrigger Binding="{Binding IsMetric,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" Value="True">
                            <Setter Property="Text" Value="{Binding Val,Converter={StaticResource ToMetricConverter}}"></Setter>
                        </DataTrigger>
                        <DataTrigger Binding="{Binding IsMetric,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" Value="False">
                            <Setter Property="Text" Value="{Binding Val,Converter={StaticResource FromMetricConverter}}"></Setter>
                        </DataTrigger>
                    </Style.Triggers>
                </Style>
            </TextBox.Style>
        </TextBox>
    </StackPanel>
</Grid> 

我现在的结局是这样的。仍然会喜欢一个解决方案,它不需要对每个可能的值都使用DataTrigger

这与@SamTheDev发布的答案有点不同,但大致相同

xaml

用法:

<mynamespace:MeasurementTextBox Measurement="{Binding SomeLength, Mode=TwoWay}"
                                UseMetric="{Binding IsMetric}"/>


创建自定义控件。这将是一个比使用转换器更优雅的解决方案。还有,当你完成后,把它贴出来,这样我就可以用了:-)注意-我已经回答了(:谢谢,除了我使用了一个转换器外,这与我提出的解决方案是一致的。对于IsMetric可能不仅仅是真的或假的情况,我仍然希望找到一个优雅的解决方案,但这肯定是一个选项。今年,我同意这有点难看:p
using System;
using System.Windows;
using System.Windows.Controls;

namespace MyNamespace.Controls
{
    /// <summary>
    /// Interaction logic for MeasurementTextBox.xaml
    /// </summary>
    public partial class MeasurementTextBox : UserControl
    {
        public MeasurementTextBox()
        {
            // This call is required by the designer.
            InitializeComponent();
        }

        public bool UseMetric {
            get { return Convert.ToBoolean(GetValue(UseMetricProperty)); }
            set { SetValue(UseMetricProperty, value); }
        }


        public static readonly DependencyProperty UseMetricProperty = DependencyProperty.Register("UseMetric", typeof(bool), typeof(MeasurementTextBox), new PropertyMetadata(MeasurementTextBox.UseMetricChanged));
        private static void UseMetricChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
        }

        public double Measurement {
            get { return (double)GetValue(MeasurementProperty); }
            set { SetValue(MeasurementProperty, value); }
        }


        public static readonly DependencyProperty MeasurementProperty = DependencyProperty.Register("Measurement", typeof(double), typeof(MeasurementTextBox), new PropertyMetadata(MeasurementTextBox.MeasurementPropertyChanged));
        private static void MeasurementPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
        }
    }
}
using System;
using System.Windows;
using System.Windows.Data;

namespace MyNamespace.Converters
{
    class MeasurementConverter : IValueConverter
    {

        const double MILLIMETERS_IN_ONE_INCH = 25.4;
        const string INCHES_ABBREVIATION = "in";
        const string MILLIMETERS_ABBREVIATION = "mm";

        const double ONE_THIRTY_SECOND = 0.03125;
        const double ONE_SIXTEENTH = 0.0625;
        const double ONE_EIGHTH = 0.125;
        const double ONE_FOURTH = 0.25;
        const double ONE_HALF = 0.5;

        const double ONE = 1;
        public double RoundToNearest(double value, int unitPrecision)
        {
            double fraction = 0;
            int reciprocal = 0;

            switch (unitPrecision)
            {
                case 0:
                    fraction = ONE;
                    reciprocal = (int)ONE;
                    break;
                case 1:
                    fraction = ONE;
                    reciprocal = (int)ONE;
                    break;
                case 2:
                    fraction = ONE_HALF;
                    reciprocal = (int)(1 / ONE_HALF);
                    break;
                case 3:
                    fraction = ONE_FOURTH;
                    reciprocal = (int)(1 / ONE_FOURTH);
                    break;
                case 4:
                    fraction = ONE_EIGHTH;
                    reciprocal = (int)(1 / ONE_EIGHTH);
                    break;
                case 5:
                    fraction = ONE_SIXTEENTH;
                    reciprocal = (int)(1 / ONE_SIXTEENTH);
                    break;
                case 6:
                    fraction = ONE_THIRTY_SECOND;
                    reciprocal = (int)(1 / ONE_THIRTY_SECOND);
                    break;
            }

            return Math.Round(value * reciprocal, MidpointRounding.AwayFromZero) * fraction;

        }

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

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            string strValue = (string)value;
            bool isMetric = (bool)parameter;

            double enteredValue = 0;
            bool enteredValueIsImperial = false;

            if (strValue.EndsWith(INCHES_ABBREVIATION))
            {
                enteredValueIsImperial = true;
                strValue = strValue.Substring(0, strValue.Length - INCHES_ABBREVIATION.Length);
            }
            else if (strValue.EndsWith(MILLIMETERS_ABBREVIATION))
            {
                enteredValueIsImperial = false;
                strValue = strValue.Substring(0, strValue.Length - MILLIMETERS_ABBREVIATION.Length);
            }
            else if (isMetric)
            {
                enteredValueIsImperial = false;
            }
            else
            {
                enteredValueIsImperial = true;
            }

            try
            {
                enteredValue = double.Parse(strValue);
            }
            catch (FormatException)
            {
                return DependencyProperty.UnsetValue;
            }

            if (isMetric)
            {
                if (enteredValueIsImperial)
                {
                    //inches to mm
                    return RoundToNearest(enteredValue * MILLIMETERS_IN_ONE_INCH, 0);
                    //0 is mm
                }
                else
                {
                    //mm to mm
                    return RoundToNearest(enteredValue, 0);
                    //0 is mm
                }
            }
            else
            {
                if (enteredValueIsImperial)
                {
                    //inches to inches
                    return RoundToNearest(enteredValue, 5);
                }
                else
                {
                    //mm to inches
                    return RoundToNearest(enteredValue / MILLIMETERS_IN_ONE_INCH, 5);
                }
            }
        }
    }
}
<mynamespace:MeasurementTextBox Measurement="{Binding SomeLength, Mode=TwoWay}"
                                UseMetric="{Binding IsMetric}"/>