Question

View:

Playing with a basic calculator using WPF(MVVM). I've 1 TextBox for the first num, 1 TextBox for the second num, 1 TextBlock for the results and 1 Button to execute the AddCommand and return the result. What's the right XAML syntax to bind these controls to the right Data.

Model:

public class Operation : INotifyPropertyChanged
{
    private double _result;
    public Operation()
    {
        _result = 0;
    }

    public double Result
    {
        get { return _result; }
        set
        {
            if (value != _result)
            {
                _result = value;
                RaisePropertyChanged("Result");
            }
        }
    }

    public double DoAdd(double first, double second)
    {
        _result = first + second;
        return _result;
    }
}

ViewModel:

public class CalcViewModel
{
    private Operation _operation;
    public RelayCommand AddCommand { get; set; }

    public CalcViewModel()
    {
        _operation = new Operation();

        // This is not correct, how to define the AddCommand here so it takes two params
        // The first and second nums to work with.
        AddCommand = new RelayCommand(first, second => ExecuteAddCommand(first, second));
    }

    private void ExecuteAddCommand(double first, double second)
    {

        // How to bind this returned double to the TextBlock in View
        _oepration.DoAdd(first, second);
    }
}

EDIT new version of code on request of Vlad

Model:

public class Operation
    {
        private double _result;

        public Operation()
        {
            _result = 0;
        }

        public double Result
        {
            get { return _result; }
        }

        public void PerformAdd(double leftNum, double rightNum)
        {
            _result = leftNum + rightNum;
        }
    }

ViewModel:

 public class CalcViewModel
    {
        private Operation _operation;
        public double LeftNumber { get; set; }
        public double RightNumber { get; set; }
        public double Result { get; set; }

        public RelayCommand AddCommand { get; set; }

        public CalcViewModel()
        {
            AddCommand = new RelayCommand(a => ExecuteAddCommand());
            _operation = new Operation();
        }

        private void ExecuteAddCommand()
        {
            _operation.PerformAdd(LeftNumber, RightNumber);
            Result = _operation.Result;
        }

View XAML:

<TextBox Text="{Binding LeftNumber}" />
<TextBox Text="{Binding RightNumber}" />
<TextBox Text="{Binding Result}" />
<Button Content="Add" Command="{Binding AddCommand}" />

View Code behind:

public partial class CalcUserControl : UserControl
{
    CalcViewModel vm;

    public CalcUserControl()
    {
        InitializeComponent();
        vm = new CalcViewModel();
        this.DataContext = vm;
    }
}

I tried all modes of binding without any result. I have here an additional question, what's the default binding mode in such a situation?

I even thought that it has to do with the datatype of the calculation, so I swiched from double to int, but still not working.

Était-ce utile?

La solution

Well, let's see what can be done.

1) Model. The model doesn't need anything fancy. I would keep it simple and make it just return the value and not use NotifyPropertyChanged. After all, it's a model.

public class BinaryOperation
{
    double _l, _r, _result = 0.0;
    public Operation(double l, double r)
    {
        _l = l; _r = r;
    }

    public double Result
    {
        get { return _result; }
    }

    public PerformAdd()
    {
        _result = _l + _r;
    }
}

2) ViewModel. Here, your RelayCommand doesn't really need any arguments. But you need to store the values of operands in your VM, so that your view can bind to them, instead of sending them in a command. Remember, business logic doesn't belong to view, view just blindly binds to the VM! So you need 3 DPs (left addend, right addend, result) in your VM.

3) When the command arrives, you just take the addends from VM, ask the model to perform the operation, retrieve the result and assign it to your VM's result DP. (Right now, your model operations are fast, so you don't need to do it in asynchronous way. But maybe in the future...)

4) View. You need for your Window/UserControl just to bind to the VM's properties. Its going to be something as simple as:

<TextBox Text="{Binding LeftAddend}"/>
<TextBox Text="{Binding RightAddend}"/>
<TextBox Text="{Binding Result}"/>
<Button Command="{Binding AddCommand}">Add</Button>

(Don't forget to set the DataContext right.)

Edit:
the VM class has to be a dependency object! And the properties should b defined as dependency properties. Something like this:

public class CalcViewModel : DependencyObject
{
    private Operation _operation;

    public double LeftNumber
    {
        get { return (double)GetValue(LeftNumberProperty); }
        set { SetValue(LeftNumberProperty, value); }
    }

    public static readonly DependencyProperty LeftNumberProperty = 
        DependencyProperty.Register("LeftNumber", typeof(double), typeof(CalcViewModel));

    public double RightNumber
    {
        get { return (double)GetValue(RightNumberProperty); }
        set { SetValue(RightNumberProperty, value); }
    }

    public static readonly DependencyProperty RightNumberProperty = 
        DependencyProperty.Register("RightNumber", typeof(double), typeof(CalcViewModel));

    public double Result
    {
        get { return (double)GetValue(ResultProperty); }
        set { SetValue(ResultProperty, value); }
    }

    public static readonly DependencyProperty ResultProperty = 
        DependencyProperty.Register("Result", typeof(double), typeof(CalcViewModel));

    public RelayCommand AddCommand { get; set; }

    public CalcViewModel()
    {
        AddCommand = new RelayCommand(a => ExecuteAddCommand());
        _operation = new Operation();
    }

    private void ExecuteAddCommand()
    {
        _operation.PerformAdd(LeftNumber, RightNumber);
        Result = _operation.Result;
    }
}

Or, if you want to do it with INotifyPropertyChanged, and you are working with .NET 4.5

public class CalcViewModel : INotifyPropertyChanged
{
    private Operation _operation;

    public event PropertyChangedEventHandler PropertyChanged;

    void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }

    double _leftNumber;
    public double LeftNumber
    {
        get { return _leftNumber; }
        set
        {
            if (value == _leftNumber) return;
            _leftNumber = value;
            NotifyPropertyChanged();
        }
    }

    double _rightNumber;
    public double RightNumber
    {
        get { return _rightNumber; }
        set
        {
            if (value == _rightNumber) return;
            _rightNumber = value;
            NotifyPropertyChanged();
        }
    }

    double _result;
    public double Result
    {
        get { return _result; }
        set
        {
            if (value == _result) return;
            _result = value;
            NotifyPropertyChanged();
        }
    }

    public RelayCommand AddCommand { get; set; }

    public CalcViewModel()
    {
        AddCommand = new RelayCommand(a => ExecuteAddCommand());
        _operation = new Operation();
    }

    private void ExecuteAddCommand()
    {
        _operation.PerformAdd(LeftNumber, RightNumber);
        Result = _operation.Result;
    }
}

The same with older .NET versions:

    void NotifyPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }

    double _leftNumber;
    public double LeftNumber
    {
        get { return _leftNumber; }
        set
        {
            if (value == _leftNumber) return;
            _leftNumber = value;
            NotifyPropertyChanged("LeftNumber");
        }
    }

etc.

Autres conseils

Thank you all and especially @Vlad. Just one tiny fault, y've declared the property Result twice on class CalcViewModel : DependencyObject.

It works now fine :)

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top