我正在使用 WPF 和 MVVM 模式构建一个复利计算器。我的视图中有一个组合框,允许用户选择复合间隔(例如“每日”、“每周”、“每月”等)。此 ComboBox 的 ItemsSource 绑定到我的视图模型中的 ObservableCollection 属性,并且 SelectedItem 绑定到 SelectedCompoundInterval 属性。
当用户从 ComboBox 中进行选择时,我希望 SelectedCompoundInterval 属性的值会相应更新。但是,当我在视图模型中调用使用此属性作为计算输入的计算方法时,SelectedCompoundInterval 属性的值始终为空。
MyViewModel 类
namespace CompoundInterestCalculator
{
internal class MyViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private Calculations _calculations = new Calculations();
private bool _showResults;
private decimal _initialAmount;
public decimal InitialAmount
{
get { return _initialAmount; }
set
{
_initialAmount = value;
Debug.WriteLine(_initialAmount.ToString());
OnPropertyChanged(nameof(InitialAmount));
}
}
private decimal _interestRate;
public decimal InterestRate
{
get { return _interestRate; }
set
{
_interestRate = value;
OnPropertyChanged(nameof(InterestRate));
}
}
private int _years;
public int Years
{
get { return _years; }
set
{
_years = value;
OnPropertyChanged(nameof(Years));
}
}
private int _months;
public int Months
{
get { return _months; }
set
{
_months = value;
OnPropertyChanged(nameof(Months));
}
}
public ObservableCollection<string> CompoundIntervals { get; } = new ObservableCollection<string>
{
"Daily","Weekly", "Monthly", "Quarterly", "Yearly"
};
private string _selectedCompoundInterval;
public string SelectedCompoundInterval
{
get { return _selectedCompoundInterval; }
set
{
if (_selectedCompoundInterval != value)
{
_selectedCompoundInterval = value;
OnPropertyChanged(nameof(SelectedCompoundInterval));
Debug.WriteLine("SelectedCompoundInterval changed: " + value);
}
}
}
public ObservableCollection<string> InterestRateTypes { get; } = new ObservableCollection<string>
{
"Percent", "Decimal"
};
private string _selectedInterestRateType;
public string SelectedInterestRateType
{
get { return _selectedInterestRateType; }
set
{
_selectedInterestRateType = value;
OnPropertyChanged(nameof(SelectedInterestRateType));
}
}
public DataTemplate CurrentTemplate
{
get
{
if (_showResults)
return (DataTemplate)Application.Current.FindResource("ResultsTemplate");
else
return (DataTemplate)Application.Current.FindResource("InputTemplate");
}
}
public void Calculate()
{
Debug.WriteLine("CompoundIntervals: " + string.Join(", ", CompoundIntervals));
Debug.WriteLine("SelectedCompoundInterval: " + SelectedCompoundInterval);
// Perform calculations here
decimal initialAmount = InitialAmount;
decimal interestRate = InterestRate;
int years = Years;
int months = Months;
string compoundInterval = SelectedCompoundInterval;
Debug.WriteLine("compoundInterval: " + compoundInterval);
string interestRateType = SelectedInterestRateType;
decimal totalAmount = _calculations.CalculationsAmounts(initialAmount, interestRate, years, months, compoundInterval, interestRateType);
// Show results
_showResults = true;
OnPropertyChanged(nameof(CurrentTemplate));
}
}
}
计算课
namespace CompoundInterestCalculator
{
internal class Calculations
{
public decimal CalculationsAmounts(decimal initialAmount, decimal interestRate, int years, int months, string compoundInterval, string interestRateType)
{
Debug.WriteLine("compoundInterval: " + compoundInterval);
int compoundFrequency;
switch (compoundInterval)
{
case "Daily":
compoundFrequency = 365;
break;
case "Weekly":
compoundFrequency = 52;
break;
case "Monthly":
compoundFrequency = 12;
break;
case "Quarterly":
compoundFrequency = 4;
break;
case "Yearly":
compoundFrequency = 1;
break;
default:
throw new ArgumentException("Invalid compound interval");
}
if (interestRateType == "Percent")
{
interestRate /= 100;
}
decimal totalAmount = initialAmount * (decimal)Math.Pow((double)(1 + interestRate / compoundFrequency), years * compoundFrequency + months * (compoundFrequency / 12));
return totalAmount;
}
}
}
MainWindow.xaml.cs代码
namespace CompoundInterestCalculator
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private MyViewModel _myViewModel;
public MainWindow()
{
InitializeComponent();
DataContext = new MyViewModel();
_myViewModel = new MyViewModel();
}
private void CalculateButton_Click(object sender, RoutedEventArgs e)
{
MyContentControl.ContentTemplate = (DataTemplate)FindResource("ResultsTemplate");
_myViewModel.Calculate();
}
private void BackButton_Click(object sender, RoutedEventArgs e)
{
MyContentControl.ContentTemplate = (DataTemplate)FindResource("InputTemplate");
}
private void NumericOnly(object sender, TextCompositionEventArgs e)
{
e.Handled = IsTextNumeric(e.Text);
}
private static bool IsTextNumeric(string str)
{
System.Text.RegularExpressions.Regex reg = new System.Text.RegularExpressions.Regex("[^0-9]");
return reg.IsMatch(str);
}
private void NumericDecimalOnly(object sender, TextCompositionEventArgs e)
{
TextBox textBox = sender as TextBox;
if (textBox != null)
{
string newText = textBox.Text.Insert(textBox.CaretIndex, e.Text);
decimal value;
if (!decimal.TryParse(newText, out value) || value < 0.01m || value > 99.9999999m)
{
e.Handled = true;
}
}
}
private void NumericMonthlyOnly(object sender, TextCompositionEventArgs e)
{
TextBox textBox = sender as TextBox;
if (textBox != null)
{
string newText = textBox.Text.Insert(textBox.CaretIndex, e.Text);
int value;
if (!int.TryParse(newText, out value) || value < 1 || value > 12)
{
e.Handled = true;
}
}
}
}
}
MainWindow.xaml ComboBox 代码
<ComboBox Grid.Column="1" ItemsSource="{Binding CompoundIntervals}" SelectedItem="{Binding SelectedCompoundInterval, Mode=TwoWay}" Margin="10,5,0,0" Width="100" Height="20">
</ComboBox>
我尝试在多个地方调试程序,我注意到 ObservableCollection 正确填充了组合框,并且在调试 SelectedCompoundInterval 属性时,每次我在程序内更改它时,它都会通知我,在我的计算中使用的compoundInterval字符串应该具有要存储的东西。
我认为这意味着问题出在该点和我计算时之间的某个位置,因为调试计算代码中的compoundInterval字符串总是显示一个Null值,然后switch语句抛出一个argumentException,因为它不适合任何情况空值。