让我们有一个例子来清楚地理解我的问题:
我有三个TextBox控件,如下所示:
<TextBox x:Name="Quantity" />
<TextBox x:Name="Rate" />
<TextBox x:Name="Amount" />
所以,我想在数量或费率变化时计算金额。 金额=数量*费率
同样,如果用户打算更改金额,我想更改费率。所以,Rate的公式将是 费率=金额/数量
实际问题:
我有一个包含三个列的DataGrid:
<DataGrid ItemsSource="{Binding OpeningBalances}">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Quantity" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Quantity}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox Text="{Binding Quantity}" Loaded="TextBox_Loaded"/>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Rate" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Rate}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox Text="{Binding Rate}" Loaded="TextBox_Loaded"/>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Amount" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock>
<TextBlock.Text>
<MultiBinding Converter="{StaticResource amountFromQuantityAndRateConverter}">
<Binding Path="Quantity" />
<Binding Path="Rate" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox Loaded="TextBox_Loaded">
<TextBox.Text>
<MultiBinding Converter="{StaticResource amountFromQuantityAndRateConverter}">
<Binding Path="Quantity" />
<Binding Path="Rate" />
</MultiBinding>
</TextBox.Text>
</TextBox>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
ViewModel中的属性声明:
public MainWindowViewModel()
{
OpeningBalances = new ObservableCollection<ItemOpeningBalanceRow>();
}
private ObservableCollection<ItemOpeningBalanceRow> _openingBalances;
public ObservableCollection<ItemOpeningBalanceRow> OpeningBalances
{
get
{
return _openingBalances;
}
set
{
_openingBalances = value;
OnPropertyChanged("OpeningBalances");
}
}
ItemOpeningBalnceRow.cs:
public class ItemOpeningBalanceRow
{
public double Quantity { get; set; }
public double Rate { get; set; }
public double Amount { get; set; }
}
转换器 - AmountFromQuantityAndRateConverter.cs
public class AmountFromQuantityAndRateConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
int _quantity = 0;
int _rate = 0;
if (int.TryParse(values[0].ToString(), out _quantity) && int.TryParse(values[1].ToString(), out _rate))
{
return _quantity * _rate;
}
return 0;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
我使用了上面提到的多值转换器但仍然是Amount列始终为null。我的意思是它不显示任何数据。
更新
@ sky-dev给出的答案是最好的答案,并且工作正常。但我想使用转换器获得相同的方法。
我尝试了什么?:
我创建了两个转换器,如下所示:
RateFromAmountAndQuantityConverter.cs
public class RateFromAmountAndQuantityConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
int _quantity = 0;
int _amount = 0;
int _rate = 0;
if (int.TryParse(values[0].ToString(), out _quantity) && int.TryParse(values[1].ToString(), out _amount) && int.TryParse(values[2].ToString(), out _rate))
{
if (_quantity != 0 && _amount != 0)
{
if (_rate == _amount / _quantity)
return _rate.ToString();
else
return (_amount / _quantity).ToString();
}
}
return "0";
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
其他是AmountFromQuantityAndRateConverter.cs
public class AmountFromQuantityAndRateConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
int _quantity = 0;
int _rate = 0;
int _amount = 0;
if (int.TryParse(values[0].ToString(), out _quantity) && int.TryParse(values[1].ToString(), out _rate) && int.TryParse(values[2].ToString(), out _amount))
{
if (_rate != 0)
{
if (_amount == _quantity * _rate)
return _amount.ToString();
else
return (_quantity * _rate).ToString();
}
}
return "0";
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
我称他们如下:
<DataGridTemplateColumn Header="Quantity" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Quantity, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox Text="{Binding Quantity, UpdateSourceTrigger=PropertyChanged}" Loaded="TextBox_Loaded"/>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Rate" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock>
<TextBlock.Text>
<MultiBinding Converter="{StaticResource rateFromAmountAndQuantityConverter}">
<Binding Path="Quantity" UpdateSourceTrigger="PropertyChanged"/>
<Binding Path="Amount" UpdateSourceTrigger="PropertyChanged"/>
<Binding Path="Rate" UpdateSourceTrigger="PropertyChanged" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox Loaded="TextBox_Loaded">
<TextBox.Text>
<MultiBinding Converter="{StaticResource rateFromAmountAndQuantityConverter}" >
<Binding Path="Quantity" UpdateSourceTrigger="PropertyChanged"/>
<Binding Path="Amount" UpdateSourceTrigger="PropertyChanged"/>
<Binding Path="Rate" UpdateSourceTrigger="PropertyChanged" />
</MultiBinding>
</TextBox.Text>
</TextBox>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Amount" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock>
<TextBlock.Text>
<MultiBinding Converter="{StaticResource amountFromQuantityAndRateConverter}">
<Binding Path="Quantity" UpdateSourceTrigger="PropertyChanged"/>
<Binding Path="Rate" UpdateSourceTrigger="PropertyChanged"/>
<Binding Path="Amount" UpdateSourceTrigger="PropertyChanged" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox Loaded="TextBox_Loaded">
<TextBox.Text>
<MultiBinding Converter="{StaticResource amountFromQuantityAndRateConverter}">
<Binding Path="Quantity" UpdateSourceTrigger="PropertyChanged"/>
<Binding Path="Rate" UpdateSourceTrigger="PropertyChanged"/>
<Binding Path="Amount" UpdateSourceTrigger="PropertyChanged" />
</MultiBinding>
</TextBox.Text>
</TextBox>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
使用以上两个转换器的问题:
他们引用彼此,如循环引用,但我没有得到stackOverflow的例外。事实上,我没有得到任何错误,但无论我设定什么价格或金额都保持为0.我也知道为什么他们仍然是0.但不知道解决方案。
答案 0 :(得分:1)
干得好,包括所有细节。真有帮助!有一些麻烦点。
试试这个。
<Grid>
<Grid.Resources>
<converters:AmountFromQuantityAndRateConverter x:Key="amountFromQuantityAndRateConverter" />
</Grid.Resources>
<DataGrid ItemsSource="{Binding OpeningBalances}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Quantity" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Quantity, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox Text="{Binding Quantity, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Rate" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Rate, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox Text="{Binding Rate, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Amount" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock>
<TextBlock.Text>
<MultiBinding Converter="{StaticResource amountFromQuantityAndRateConverter}">
<Binding Path="Quantity" UpdateSourceTrigger="PropertyChanged" />
<Binding Path="Rate" UpdateSourceTrigger="PropertyChanged" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
public partial class MainWindow : Window, INotifyPropertyChanged
{
public MainWindow()
{
InitializeComponent();
OpeningBalances = new ObservableCollection<ItemOpeningBalanceRow>(new [] { new ItemOpeningBalanceRow()});
DataContext = this;
}
private ObservableCollection<ItemOpeningBalanceRow> _openingBalances;
public ObservableCollection<ItemOpeningBalanceRow> OpeningBalances
{
get
{
return _openingBalances;
}
set
{
_openingBalances = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public class ItemOpeningBalanceRow : INotifyPropertyChanged
{
private double _quantity;
private double _rate;
public double Quantity
{
get { return _quantity; }
set { _quantity = value; OnPropertyChanged(); }
}
public double Rate {
get { return _rate; }
set { _rate = value; OnPropertyChanged(); }
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public class AmountFromQuantityAndRateConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
int _quantity = 0;
int _rate = 0;
if (int.TryParse(values[0].ToString(), out _quantity) && int.TryParse(values[1].ToString(), out _rate))
{
return (_quantity * _rate).ToString();
}
return 0;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
上面的代码应该有用,但我会推荐另一种方法。只需抛弃转换器并在ViewModel(ItemOpeningBalanceRow)中执行此操作。更简单。这是什么样的:
<DataGrid ItemsSource="{Binding OpeningBalances}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Quantity" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Quantity, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox Text="{Binding Quantity, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Rate" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Rate, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox Text="{Binding Rate, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Amount" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Amount}">
</TextBlock>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
public class ItemOpeningBalanceRow : INotifyPropertyChanged
{
private double _quantity;
private double _rate;
public double Quantity
{
get { return _quantity; }
set { _quantity = value; OnPropertyChanged(); OnPropertyChanged("Amount"); }
}
public double Rate {
get { return _rate; }
set { _rate = value; OnPropertyChanged(); OnPropertyChanged("Amount"); }
}
public double Amount
{
get { return Quantity*Rate; }
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
*更新*
我已经计算了金额的费率。
<DataGrid ItemsSource="{Binding OpeningBalances}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Quantity" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Quantity, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox Text="{Binding Quantity, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Rate" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Rate, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox Text="{Binding Rate, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Amount" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Amount, UpdateSourceTrigger=PropertyChanged}">
</TextBlock>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox Text="{Binding Amount, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
public class ItemOpeningBalanceRow : INotifyPropertyChanged
{
private double _quantity;
private double _rate;
private double _amount;
public double Quantity
{
get { return _quantity; }
set
{
_quantity = value;
OnPropertyChanged();
Amount = Quantity*Rate;
}
}
public double Rate {
get { return _rate; }
set
{
if (!(Math.Abs(_rate - value) > 0.0001)) return;
_rate = value;
OnPropertyChanged();
Amount = Quantity * Rate;
}
}
public double Amount
{
get { return Quantity*Rate; }
set
{
if (!(Math.Abs(_amount - value) > 0.0001)) return;
_amount = value;
OnPropertyChanged();
Rate = _amount / Quantity;
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
答案 1 :(得分:1)
要使用转换器,您只能使用1个转换器。您需要使转换器中数量字段的范围处于类级别,以便它可以保留数量值以计算ConvertBack调用的新速率。我的模型现在没有Amount属性,但如果您需要任何业务逻辑,那么您只需要一个返回Quantity * Rate的getter,因为Amount将始终等于该结果。
AmountConverter - 我使用了小数,但您可以更改为int并处理舍入,但是您决定。
public class AmountConverter : IMultiValueConverter
{
decimal quantity = 0m;
decimal rate = 0m;
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (decimal.TryParse(values[0].ToString(), out quantity) && decimal.TryParse(values[1].ToString(), out rate))
{
return (quantity * rate).ToString();
}
return "0";
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
decimal amount = 0m;
object[] values = new object[2];
values[0] = quantity;
values[1] = rate;
if (decimal.TryParse(value.ToString(), out amount))
{
if (quantity != 0m)
values[1] = amount / quantity;
}
return values;
}
}
XAML - 我简化了使用一些TextBlock来演示功能。如果数量为0,则输入新的金额会将金额重置为0,因为您不能除以0。
<StackPanel DataContext="{StaticResource MainViewModel}">
<TextBox Text="{Binding Quantity, UpdateSourceTrigger=PropertyChanged}"
Margin="4" />
<TextBox Text="{Binding Rate, UpdateSourceTrigger=PropertyChanged}"
Margin="4" />
<TextBox Margin="4">
<TextBox.Text>
<MultiBinding Converter="{StaticResource AmountConverter}"
UpdateSourceTrigger="PropertyChanged">
<Binding Path="Quantity" />
<Binding Path="Rate" />
</MultiBinding>
</TextBox.Text>
</TextBox>
</StackPanel>