开发者

Unable to use IMultiValueConverter in WPF to allow metric or imperial input

开发者 https://www.devze.com 2023-01-05 19:07 出处:网络
I would like to create a C# WPF form that collects measurements of equipment, but some users insit on using feet and inches for measurements while other (younger) users do everything in metric. The da

I would like to create a C# WPF form that collects measurements of equipment, but some users insit on using feet and inches for measurements while other (younger) users do everything in metric. The database stores everything in meters, and I figured this was a perfect place to use a IMultiValueConverter.

Each form has a DependencyProperty called UseImperialMeasurements that stores the users preference, and I pass this value to the Convert function along with the actual measurement in meters. The code so far looks like this:

<Label>Length:</Label>
<TextBox>
    <TextBox.Text>
        <MultiBinding Converter="{StaticResource DistanceStringConverter}" Mode="TwoWay">
            <Binding Path="Length" />
            <Binding ElementName="ThisControl" Path="UseImpe开发者_JAVA技巧rialMeasurements" />
        </MultiBinding>
    </TextBox.Text>
</TextBox>


public class DistanceStringConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if (values.Length != 2 || !(values[0] is Double) || !(values[1] is Boolean)) return "ERROR";

        Double measurement = (Double)values[0];   // Always in meters
        Boolean useImperial = (Boolean)values[1];
        string unit;

        if (useImperial == true)
        {
            double feet, inches;

            inches = (measurement * 100) / 2.54;

            feet = Math.Floor(inches / 12);
            inches = inches % 12;

            var replacements = new object[4];

            replacements[0] = feet.ToString("#,0");
            replacements[1] = "'";
            replacements[2] = inches.ToString("0");
            replacements[3] = "\"";

            // Display like 12'4"
            return string.Format("{0}{1}{2}{3}", replacements);
        }
        else
        {
            // Display like 5,987.97m
            return string.Format("{0}{1}", measurement.ToString("#,0.##"), m);                
        }
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
    {
        // ToDo: Figure out if imperial or metric measurements are prefered...
    }
}

This works well for displaying measurements, but taking them as input in either metric or imperial has been problematic as the I am unable to see the bound value of the UseImperialMeasurements property in the ConvertBack method. As such, I am not only unable to guess if the user is inputing feet or meters when no units are specified, but I have no way to know what boolean value to return so that the users set preference doesn't change.

Is it possible to pass the value of UseImperialMeasurements into the ConvertBack function?


Store the UseImperialMeasurements value as an application setting (or any place you want to, really, but the use case favors an application-scoped user setting). You get to that by double clicking on the Properties item in the Project tree in Visual Studio. There, just click the "Settings" tab and add "UseImperialMeasurements" as a bool, scoped User, and set a default value.

Then, wherever you want in your code, access and change the value with the Properties.Settings.Default.UseImperialMeasurements property.

If you remember to call Properties.Settings.Default.Save() before closing the form or the application, the setting will "Stick".

In XAML, then, you'd access the setting with this binding:

{Binding Source={x:Static my:Settings.Default},Path=UseImperialMeasurements}

Or, forget Converters. Bake this functionality into a ViewModel, and store the UseImperialMeasurements value as an application setting (or any place you want to, really, but the use case favors a user setting)

public class BackingDataClass
{
    public double LengthAlwaysMetric { get; set; }
}
public class BackingDataClassViewModel : INotifyPropertyChanged
{
    public BackingDataClass Data { get; set; }
    public double Length
    {
        get
        {
            if (Properties.Settings.Default.UseImperialMeasurements == true)
            {
                return ConvertToImperial(Data.LengthAlwaysMetric);
            }
            else
            {
                return _lengthAlwaysMetric;
            }
        }
        set
        {
            if (Properties.Settings.Default.UseImperialMeasurements == true)
            {
                Data.LengthAlwaysMetric = ConvertToMetric(value);
            }
            else
            {
                Data.LengthAlwaysMetric = value;
            }
            PropertyChanged(this, new PropertyChangedEventArgs("Length"));
        }
    }
    public event PropertyChangedEventHandler PropertyChanged;
}


Your ConvertBack method should parse the units from the string and set the UseImperialMeasurements property (via the seconds item in the returned array).

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号