2015-06-10 34 views
0

我想创建一个TextBox,可以进行测量并在必要时将其转换为不同的单位(最终结果为double)。转换将由值IsMetric控制。如果IsMetric == true"36.5 in"将变成927.1(代表毫米的双倍)。相反,如果IsMetric == false然后"927.1 mm"将变成36.5如何创建一个有条件转换用户输入的WPF文本框?

我认为经常TextBox使用IValueConverter,但ConverterParameter不是DependencyProperty,所以我不能绑定IsMetric它。

我试过IMultiValueConverterConvertBack函数只接收TextBox的当前值而不是所有的绑定值。这意味着我在转换用户输入时不知道IsMetric

我是否错过了ConvertBack函数?如果没有,那么我是否需要创建一个从TextBox派生的类?

+0

创建一个自定义** 控制**。这将比使用Converter更加优雅。此外,当你完成后,张贴它,所以我可以使用它:-) –

+0

注意 - 我已经回答了它(: –

回答

0

如果这是您唯一想做的事情,请尝试其他方式使用转换器参数。 但是,我会选择这个选项 - 如果你的文本框里有更多的逻辑,或者倾向于有更多的依赖性 - 创建从文本框继承的自定义控件,并添加你自己的依赖属性。然后你可以使用你的IsMetric,并将其转换为你想上的PropertyChanged等

3

你可以使用两个转换器之一,从度量和另一转换成公制:

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> 
+0

谢谢,这是我提出的解决方案的线,除了我用一个单一的转换器。我仍然希望为IsMetric不仅仅是True或False的情况寻找一个优雅的解决方案,但这当然是一个选择。 – bzuillsmith

+0

年,我确实认为这有点不好:p – Usama

0

我结束了这些方针的东西现在。仍然会享受不需要每个可能值都有DataTrigger的解决方案。

这与@SamTheDev发布的答案有点不同,但沿着相同的路线。

XAML

<UserControl x:Class="MyNamespace.Controls.MeasurementTextBox" 
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
      xmlns:c="clr-namespace:MyNamespace.Converters" 
      xmlns:b="clr-namespace:MyNamespace.Behaviors" 
      xmlns:sys="clr-namespace:System;assembly=mscorlib" 
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
      mc:Ignorable="d" 
      x:Name="root"> 
    <UserControl.Resources> 
     <c:MeasurementUnitConverter x:Key="muc"/> 
     <c:MeasurementConverter2 x:Key="mc"/> 
     <sys:Boolean x:Key="BooleanFalse">False</sys:Boolean> 
     <sys:Boolean x:Key="BooleanTrue">True</sys:Boolean> 
    </UserControl.Resources> 
    <Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch"> 
     <Grid.ColumnDefinitions> 
      <ColumnDefinition Width="*"/> 
      <ColumnDefinition Width="30"/> 
     </Grid.ColumnDefinitions> 
     <TextBox Margin="0" VerticalContentAlignment="Center" HorizontalAlignment="Stretch" HorizontalContentAlignment="Right" VerticalAlignment="Stretch" 
       b:AutoSelectBehavior.AutoSelect="True"> 
      <TextBox.Style> 
       <Style TargetType="{x:Type TextBox}"> 
        <Style.Triggers> 
         <DataTrigger Binding="{Binding UseMetric, ElementName=root}" Value="True"> 
          <Setter Property="Text" Value="{Binding Measurement, Mode=TwoWay, ElementName=root, Converter={StaticResource mc}, ConverterParameter={StaticResource BooleanTrue}}"></Setter> 
         </DataTrigger> 
         <DataTrigger Binding="{Binding UseMetric, ElementName=root}" Value="False"> 
          <Setter Property="Text" Value="{Binding Measurement, Mode=TwoWay, ElementName=root, Converter={StaticResource mc}, ConverterParameter={StaticResource BooleanFalse}}"></Setter> 
         </DataTrigger> 
        </Style.Triggers> 
       </Style> 
      </TextBox.Style> 
     </TextBox> 
     <!-- in or mm label --> 
     <Label VerticalAlignment="Center" Padding="0" Margin="5" HorizontalAlignment="Left" Grid.Column="1" 
       Content="{Binding UseMetric, ElementName=root, Converter={StaticResource muc}}"/> 
    </Grid> 
</UserControl> 

xaml.cs

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}"/> 
相关问题