作爲一個有點人爲的例子考慮一個簡單的外匯計算器有兩種不同貨幣的金額和一個匯率之間進行轉換。然後,規則是在任何一個金額改變時計算利率,如果利率改變,則第二金額是從第一金額和匯率中計算出來的。如何避免在WPF中使用數據綁定時的遞歸循環?
下面的實現具有視圖模型中的所有交互邏輯,更改GUI中的任何數量都會導致相互遞歸循環。
嘗試修復它的一種方法是在模型的setter上添加檢查,以便在將屬性設置爲其現有值時不會引發事件,這在任何情況下都是很好的做法。然而,對於浮點數來說,這本身並不是一個萬無一失的解決方案,因此總會有一個小的舍入誤差,從而導致事件發生。
在沒有數據綁定的世界中,對模型和其他文本框的更新可以在文本框的LostFocus事件中完成,該事件不會觸發任何其他事件,因爲我們只響應用戶事件而不會更改數據。
我想到的另一種方法是使用標誌來指示某個字段正在以編程方式更新,並且在設置標誌時忽略對該字段的更改,但當涉及很多字段時很快就會變得雜亂無章。
是否有任何標準技術或模式用於解決WPF應用程序中的這個問題?
視圖模型
namespace LoopingUpdates
{
public class FxModel : INotifyPropertyChanged
{
private double _amountCcy1;
private double _amountCcy2;
private double _rate;
public double AmountCcy1
{
get { return _amountCcy1; }
set
{
_amountCcy1 = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("AmountCcy1"));
}
}
public double AmountCcy2
{
get { return _amountCcy2; }
set
{
_amountCcy2 = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("AmountCcy2"));
}
}
public double Rate
{
get { return _rate; }
set
{
_rate = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Rate"));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
public class ViewModel
{
public FxModel FxModel { get; set; }
public ViewModel()
{
FxModel = new FxModel() { AmountCcy1 = 100, AmountCcy2 = 200, Rate = 2 };
FxModel.PropertyChanged += FxModel_PropertyChanged;
}
private void FxModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
switch (e.PropertyName) {
case "AmountCcy1":
Debug.WriteLine("Amount Ccy 1 changed");
FxModel.Rate = FxModel.AmountCcy2/FxModel.AmountCcy1;
break;
case "AmountCcy2":
Debug.WriteLine("Amount Ccy 2 changed");
FxModel.Rate = FxModel.AmountCcy2/FxModel.AmountCcy1;
break;
case "Rate":
Debug.WriteLine("Rate 1 changed");
FxModel.AmountCcy2 = FxModel.AmountCcy1 * FxModel.Rate;
break;
}
}
}
}
窗口XAML
<Window x:Class="LoopingUpdates.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:LoopingUpdates"
mc:Ignorable="d"
Title="MainWindow" Height="148.7" Width="255.556" Loaded="Window_Loaded">
<Grid>
<Label x:Name="label" Content="Amount Ccy 1" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top"/>
<Label x:Name="label1" Content="Amount Ccy 2" HorizontalAlignment="Left" Margin="10,41,0,0" VerticalAlignment="Top"/>
<Label x:Name="label2" Content="Rate" HorizontalAlignment="Left" Margin="10,72,0,0" VerticalAlignment="Top"/>
<TextBox x:Name="txtAmountCcy1" Text="{Binding FxModel.AmountCcy1}" HorizontalAlignment="Left" Height="26" Margin="99,10,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="72" />
<TextBox x:Name="txtAmountCcy2" Text="{Binding FxModel.AmountCcy2}" HorizontalAlignment="Left" Height="26" Margin="99,41,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="72" />
<TextBox x:Name="txtRate" Text="{Binding FxModel.Rate}" HorizontalAlignment="Left" Height="26" Margin="99,72,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="72" />
</Grid>
</Window>
背後
namespace LoopingUpdates
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
DataContext = new ViewModel();
}
}
}
如果你擔心舍入誤差,則在設置器中使用布爾標誌。對於你在這裏做的事情,你不需要每個屬性都有一個單獨的標誌,只有一個由所有屬性共享的類級標誌。 –