WPF MVVM模式 - 控件绑定无法正常工作

时间:2016-03-22 16:05:22

标签: wpf mvvm

我是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);
        }
    }

1 个答案:

答案 0 :(得分:2)

您绑定到CalculatorViewModel.Result,该值在值更改时不会引发属性更改通知。 WPF绑定使用通知来了解它们何时应该更新。

两种解决方案:

  1. 绑定到CalculatorModel.Result而不是CalculatorViewModel.Result

    <TextBox Name="Answer" Text="{Binding CalculatorModel.Result}" .. />
    
  2. 每当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");
    }
    
  3. 通常我会使用#1,除非有充分的理由不这样做。