2015-06-10 33 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}"/> 
相關問題