我想为我的Xamarin和Wpf项目提供一个转换器系统。我不想在数据库中保存任何单位,因此我想在用户更改单位时直接转换文本框值。
我公开了一些类似的可观察的收藏;
public class AreaList : ObservableCollection<Unit>
{
public AreaList() : base()
{
Add(new Unit("mm²"));
Add(new Unit("cm²"));
Add(new Unit("dm²"));
Add(new Unit("m²"));
}
}
public class Unit
{
private string name;
public Unit(string name)
{
this.name = name;
}
public string Name
{
get { return name; }
set { name = value; }
}
}
在视图中,我将集合绑定到我的组合框。我给我的TextBox他绑定属性的名称(Text =“ {Binding TxtBoxValue}” => x:Name =“ TxtBoxValue”)。 ConvertUnitValueCommand在视图模型中将此名称设置为字符串,以了解在更改单位时转换器函数应使用哪个变量。
查看
<UserControl.Resources>
<c:AreaList x:Key="AreaListData" />
</UserControl.Resources>
<TextBox x:Name="TxtBoxValue"
Text="{Binding Mode=TwoWay, Path=TxtBoxValue, UpdateSourceTrigger=PropertyChanged}">
</TextBox>
<ComboBox IsSynchronizedWithCurrentItem="True"
IsEditable="False"
DisplayMemberPath="Name"
SelectedItem="{Binding Unit,Mode=OneWayToSource}"
ItemsSource="{Binding Source={StaticResource AreaListData}}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="PreviewMouseLeftButtonDown">
<i:InvokeCommandAction Command="{Binding ConvertUnitValueCommand}"
CommandParameter="{Binding ElementName=TxtBoxValue, Path=Name}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</ComboBox>
ViewModel
private string ConvertControlName;
private void ConvertUnitValue(object obj)
{
ConvertControlName = obj.ToString();
}
public Unit Unit
{
get => Get<Unit>();
set
{
if (ConvertControlName != null)
{
FieldInfo variable = this.GetType().GetField(ConvertControlName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.Static | BindingFlags.NonPublic);
//Get the Value from setted Binding Variable
double oldValue = (double)variable.GetValue(this);
//Convert the value
if (oldValue > 0)
{
double newValue = Converts.ConvertUnitValue(Unit, value, oldValue);
variable.SetValue(this, newValue);
}
Set(value);
}
}
也许任何人都可以给我一些启发来做得更好。
我对您的代码影响了解不多,但是我建议您尝试使用MVVM模式进行以下设计,该模式可以消除UI和后端之间的紧密耦合。我在这里把东西分开了
您的XAML将具有类似的代码
<TextBox x:Name="unitTextbox"
Text="{Binding Path=Value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
</TextBox>
<ComboBox IsSynchronizedWithCurrentItem="True"
IsEditable="False"
DisplayMemberPath="Name"
SelectedItem="{Binding SelectedUnit}"
ItemsSource="{Binding AvailableUnits}">
</ComboBox>
您的ViewModel将像
public class MainVm : Observable
{
#region Private Fields
private double _value;
private ObservableCollection<Unit> _availableUnits;
private Unit _selectedUnit;
private Unit _previouslySelected;
#endregion Private Fields
#region Public Constructors
public MainVm()
{
_availableUnits = new ObservableCollection<Unit>()
{
new Unit("mm²"),
new Unit("cm²"),
new Unit("dm²"),
new Unit("m²")
};
}
#endregion Public Constructors
#region Public Properties
public double Value
{
get
{
return _value;
}
set
{
if (_value != value)
{
_value = value;
OnPropertyChanged();
}
}
}
public Unit SelectedUnit
{
get { return _selectedUnit; }
set
{
_previouslySelected = _selectedUnit;
_selectedUnit = value;
// call to value conversion function
// convert cm² to mm² or anything
Value = UnitConvertor.Convert(_value, _previouslySelected.Name, _selectedUnit.Name);
OnPropertyChanged();
}
}
public ObservableCollection<Unit> AvailableUnits => _availableUnits;
#endregion Public Properties
}
我的可观察班将是
public class Observable : INotifyPropertyChanged
{
#region Public Events
public event PropertyChangedEventHandler PropertyChanged;
#endregion Public Events
#region Protected Methods
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion Protected Methods
}
最好将枚举用于单位
以下示例对基本单元m²的用户输入进行标准化:
Unit.cs
public class Unit
{
public Unit(string name, decimal baseFactor)
{
this.Name = name;
this.BaseFactor = baseFactor;
}
#region Overrides of Object
/// <inheritdoc />
public override string ToString() => this.Name;
#endregion
public string Name { get; set; }
public decimal BaseFactor { get; set; }
}
ViewModel.cs
public class ViewModel : INotifyPropertyChanged
{
public ViewModel()
{
this.Units = new List<Unit>()
{
new Unit("mm²", (decimal) (1 / Math.Pow(1000, 2))),
new Unit("cm²", (decimal) (1 / Math.Pow(100, 2))),
new Unit("dm²", (decimal) (1 / Math.Pow(10, 2))),
new Unit("m²", 1)
};
}
private void NormalizeValue()
{
this.NormalizedValue = this.UnitValue * this.SelectedUnit.BaseFactor;
}
private List<Unit> units;
public List<Unit> Units
{
get => this.units;
set
{
this.units = value;
OnPropertyChanged();
}
}
private Unit selectedUnit;
public Unit SelectedUnit
{
get => this.selectedUnit;
set
{
this.selectedUnit = value;
OnPropertyChanged();
NormalizeValue();
}
}
private decimal unitValue;
public decimal UnitValue
{
get => this.unitValue;
set
{
this.unitValue = value;
OnPropertyChanged();
NormalizeValue();
}
}
private decimal normalizedValue;
public decimal NormalizedValue
{
get => this.normalizedValue;
set
{
this.normalizedValue = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
ManiWindow.xaml
<Window>
<Window.DataContext>
<ViewModel />
</Window.DatContext>
<StackPanel>
<!-- Input -->
<TextBox Text="{Binding UnitValue}"/>
<ComboBox ItemsSource="{Binding Units}"
SelectedItem="{Binding SelectedUnit}"/>
<TextBlock Text="{Binding NormalizedValue}"/>
</StackPanel>
</Window>
以下示例对基本单元m²的用户输入进行标准化:
ViewModel.cs
public class ViewModel : INotifyPropertyChanged
{
private void NormalizeValue()
{
this.NormalizedValue = this.UnitValue * this.SelectedUnit.BaseFactor;
}
private List<Unit> units;
public List<Unit> Units
{
get => this.units;
set
{
this.units = value;
OnPropertyChanged();
}
}
private Unit selectedUnit;
public Unit SelectedUnit
{
get => this.selectedUnit;
set
{
this.selectedUnit = value;
OnPropertyChanged();
NormalizeValue();
}
}
private decimal unitValue;
public decimal UnitValue
{
get => this.unitValue;
set
{
this.unitValue = value;
OnPropertyChanged();
NormalizeValue();
}
}
private decimal normalizedValue;
public decimal NormalizedValue
{
get => this.normalizedValue;
set
{
this.normalizedValue = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
ManiWindow.xaml
<Window>
<Window.DataContext>
<ViewModel />
</Window.DatContext>
<StackPanel>
<!-- Input -->
<TextBox Text="{Binding UnitValue}"/>
<ComboBox ItemsSource="{Binding Units}"
SelectedItem="{Binding SelectedUnit}"/>
<TextBlock Text="{Binding NormalizedValue}"/>
</StackPanel>
</Window>