我是WPF-MVVM模式的新手。我写了一个小应用程序,它执行两个数字的添加。当单击按钮时执行添加时,用户输入两个数字。
问题是Result
没有绑定到第三个文本框。
以下是我的代码(对MVVM不熟悉,如果您发现其他问题,请告诉我们):
App Class - Startup
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
MVVM_Math_Calc.Views.Calculator calc = new Views.Calculator ();
MVVM_Math_Calc.ViewModels.CalculatorViewModel context = new ViewModels.CalculatorViewModel ();
calc.DataContext = context;
calc.Show();
}
}
查看
<Window x:Class="MVVM_Math_Calc.Views.CalculatorWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Calculator" Height="200" Width="525">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="1*" />
<RowDefinition Height="1*" />
<RowDefinition Height="1*" />
</Grid.RowDefinitions>
<Label Content="Number 1" Grid.Column="0" Grid.Row="0" Foreground="Blue" />
<Label Content="Number 1" Grid.Column="1" Grid.Row="0" Foreground="Blue" />
<Label Content="Result" Grid.Column="2" Grid.Row="0" Foreground="Blue" />
<TextBox Name="Num1" Text="{Binding Number1}" Grid.Column="0" Grid.Row="1" BorderBrush="Gray" />
<TextBox Name="Num2" Text="{Binding Number2}" Grid.Column="1" Grid.Row="1" BorderBrush="Gray" />
<TextBox Name="Answer" Text="{Binding Result}" Grid.Column="2" Grid.Row="1" BorderBrush="Gray" />
<Button Content="Addition" Command="{Binding Path=AddNumbersCommand}" Grid.Column="0" Grid.Row="2" />
</Grid>
</Window>
CalculatorViewModel
public class CalculatorViewModel : ViewModelBase
{
private CalculatorModel calculatorModel;
private ICommand additionCommand;
public CalculatorViewModel ()
{
this.CalculatorModel = new CalculatorModel (20, 10);
}
public CalculatorModel CalculatorModel
{
get
{ return calculatorModel; }
set
{ calculatorModel = value; }
}
public int Result
{
get { return this.CalculatorModel.Result; }
set { this.CalculatorModel.Result = value; }
}
public int Number1
{
get { return this.CalculatorModel.Number1; }
set { this.CalculatorModel.Number1 = value; }
}
public int Number2
{
get { return this.CalculatorModel.Number2; }
set { this.CalculatorModel.Number2 = value; }
}
public ICommand AddNumbersCommand
{
get
{
if (additionCommand == null)
{
additionCommand = new DelegateCommand(param => AddNumbers());
}
return additionCommand;
}
}
public void AddNumbers ()
{
Debug.WriteLine ("Addition");
this.CalculatorModel.Result = this.CalculatorModel.Number1 + this.CalculatorModel.Number2;
this.Result = this.calculatorModel.Result;
}
}
CalculatorModel
public class CalculatorModel : ViewModelBase
{
private int num1;
private int num2;
private int result;
public CalculatorModel ()
{}
public CalculatorModel (int n1, int n2)
{
this.Number1 = n1;
this.Number2 = n2;
this.Result = 0;
}
public int Number1
{
get
{ return num1; }
set
{
if (value != num1)
{
num1 = value;
OnPropertyChanged("Number1");
}
}
}
public int Number2
{
get
{ return num2; }
set
{
if (value != num2)
{
num2 = value;
OnPropertyChanged ("Number2");
}
}
}
public int Result
{
get
{ return result; }
set
{
if (value != result)
{
result = value;
OnPropertyChanged ("Result");
}
}
}
}
ViewModelBase
public class ViewModelBase : INotifyPropertyChanged
{
// event handler
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged (string propertyName)
{
this.VerifyPropertyName (propertyName);
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
handler (this, new PropertyChangedEventArgs(propertyName));
}
}
public virtual void VerifyPropertyName(string propertyName)
{
// Verify that the property name matches a real,
// public, instance property on this object.
if (TypeDescriptor.GetProperties(this)[propertyName] == null)
{
string msg = "Invalid property name: " + propertyName;
throw new Exception ("Error");
}
}
}
DelegateCommand
public class DelegateCommand : ICommand
{
readonly Action<object> _execute;
readonly Predicate<object> _canExecute;
public DelegateCommand (Action<object> execute) : this (execute, null)
{}
/// <param name="canExecute">The execution status logic.</param>
public DelegateCommand (Action<object> execute, Predicate<object> canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");
_execute = execute;
_canExecute = canExecute;
}
[DebuggerStepThrough]
public bool CanExecute(object parameters)
{
return _canExecute == null ? true : _canExecute(parameters);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameters)
{
Debug.WriteLine("Execute");
_execute (parameters);
}
}
答案 0 :(得分:2)
您绑定到CalculatorViewModel.Result
,该值在值更改时不会引发属性更改通知。 WPF绑定使用通知来了解它们何时应该更新。
两种解决方案:
绑定到CalculatorModel.Result
而不是CalculatorViewModel.Result
<TextBox Name="Answer" Text="{Binding CalculatorModel.Result}" .. />
每当ViewModel.Result
更改时,都会为Model.Result
举起PropertyChanged事件。
例如:
public CalculatorModel CalculatorModel
{
get { return calculatorModel; }
set
{
// remove old event if necessary
if (calculatorModel != null)
calculatorModel.PropertyChanged -= CalculatorModel_PropertyChanged;
calculatorModel = value;
// attach a propertyChanged event to re-raise for ViewModel
if (calculatorModel != null)
calculatorModel.PropertyChanged += CalculatorModel_PropertyChanged;
}
}
private void CalculatorModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "Result")
OnPropertyChanged("Result");
}
通常我会使用#1,除非有充分的理由不这样做。