Resolved Problem With MVC Pattern - ICommand With Button

madaxe2020

Well-known member
Joined
Sep 7, 2020
Messages
50
Programming Experience
5-10
I hope somebody can help, I'm learning the M-V-VM pattern and in this example the Button should become deactivated when there is no string in the text field and when the button is selected its suppose to throw a debug assert.

There is no error , warnings it just does not work, the button seems to be detached from the view model and i dont know why

Thanks

Madaxe


Model:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Text;

namespace MVVM_Learning.Models
{
    public class CustomerModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        private string _Name;
        public string Name
        {
            get
            {
                return this._Name;
            }
            set
            {
                this._Name = value;
                NotifyPropertyChanged("Name");
            }
        }
        public CustomerModel(string CustomerName)
        {
            this.Name = CustomerName;
        }


        private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
}
View Model:
using MVVM_Learning.Commands;
using MVVM_Learning.Models;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Windows.Input;

namespace MVVM_Learning.ViewModels
{
    public class CustomerViewModel
    {
        public ICommand UpdateCommand { get; private set; }
        public CustomerModel customerModel {get; private set; }
        public bool CanUpdate
        {
            get
            {
                if(this.customerModel == null)
                {
                    return false;
                }
                return !String.IsNullOrWhiteSpace(this.customerModel.Name);
            }
        }
        public CustomerViewModel()
        {
            // Normally Get From Database
            this.customerModel = new CustomerModel("Bob");
            this.UpdateCommand = new CustomerUpdateCommand(this);
        }
        public void SaveChanges()
        {
            // Normally Save to Database
            Debug.Assert(false, String.Format("{0} Was Updated.", this.customerModel.Name));
        }
    }
}

View:
using MVVM_Learning.ViewModels;
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;

namespace MVVM_Learning.Views
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            DataContext = new CustomerViewModel();
        }
    }
}

Command:
using MVVM_Learning.ViewModels;
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Input;

namespace MVVM_Learning.Commands
{
    internal class CustomerUpdateCommand : ICommand
    {
        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }

        private CustomerViewModel _customerViewModel;
        public CustomerUpdateCommand(CustomerViewModel cusomerViewModel)
        {
            this._customerViewModel = cusomerViewModel;
        }

        public bool CanExecute(object parameter)
        {
            return this._customerViewModel.CanUpdate;
        }

        public void Execute(object parameter)
        {
            this._customerViewModel.SaveChanges();
        }
    }
}
 
NotifyPropertyChanged("Name");
Is not required to specify as you are.
Take for example in this pseudo code :
C#:
        public string LastLoginAt
        {
            get => lastLoginAt;
            set { lastLoginAt = value; OnPropertyChanged(); }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged([CallerMemberName] string new_Value = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(new_Value));
        }
OnPropertyChanged - knows what the calling code is by whatever raises its event.

In your case you are specifying where it's not required on line 10 :
C#:
        public string Name
        {
            get
            {
                return this._Name;
            }
            set
            {
                this._Name = value;
                NotifyPropertyChanged("Name");
            }
        }
While it's worth pointing that out, it not going to fix your problem. This is why we use debuggers. And you too will have to use the debugger and step into your code from the moment your application starts, and identify irregularities like so. You can find a debugging tutorial in my signature from Microsoft.

One more thing, this is wrong :
C#:
namespace MVVM_Learning.Views
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            DataContext = new CustomerViewModel();
        }
    }
}
Should Be :
C#:
namespace MVVM_Learning.Views
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {    
        public readonly CustomerViewModel CVM = new CustomerViewModel();
        public MainWindow()
        {
            InitializeComponent();
            DataContext = CVM;
        }
    }
}
Declare your models at class level. If you declare new objects at the method level, they will only live for the life-cycle of that method. And any call made to that new object will require you to call a new instance, or else you will find that your model may also be null. I've been using the same M-V-V-M for years and this is what works for me. I would also recommend you to read up on calling the new keyword. new modifier - C# Reference.
 
Mod edit.

No need to quote in whole, or quote the person in whole directly above you. :)
 
Last edited by a moderator:
There are also other minor problems which could be tweaked, which I don't have time to dig into. Sorry. But maybe someone else will take a look at some of the other pieces I didn't respond to.

What I really think would be more helpful for you, is to start using your debugger, and look at what your code is doing as it executes, and if you find more irregular behaviour, you can ask here as to why it's behaving in a particular way, and someone else will pick it up for you. I'm glad that helped you.
 
Back
Top Bottom