2
votes

I am binding a button in my XAML to update a user in my database. I want to utilize CanExecute to enable the button only after a change has been made to a property on the ViewModel.

I am using the Entity Framework and a WCF Data service to perform my CRUD operations.

Model Class

    public virtual DbSet<user> users { get; set; }

    ... 

    namespace pk.Data
    {
        using System;
        using System.Collections.Generic;

        public partial class user
        {
            public user()
            {
                this.private_account = new HashSet<private_account>();
            }

            public int id { get; set; }
            public string username { get; set; }
            public int superuser { get; set; }
            public int enabled { get; set; }

            public virtual ICollection<private_account> private_account { get; set; }
        }
    }

WCF Service

public class pkService
{

    // getUsers() - 
    //  Get all users from the database
    [OperationContract]
    public IEnumerable<user> getUsers()
    {
        using (var  context = new pkEntities())
        {
            // Needed in order to handle EF objects with Hashes 
            context.Configuration.ProxyCreationEnabled = false;
            // Pull entity object fom DB
            var results = context.users.ToList();
            // Detatch from Entity Model
            results.ForEach(e => context.Entry(e).State = EntityState.Detached);
            //Return detached object 
            return results;
        }
    }

    // updateUser(user) -
    //  Update user in the DB
    [OperationContract]
    public void updateUser(user user){
        using (var context = new pkEntities())
        {
            context.Configuration.ProxyCreationEnabled = false;
            context.Entry(user).State = EntityState.Modified;
            context.SaveChanges();
        }
    }
}

View Model #

 public class HomeViewModel: ViewModelBase
    {
        // HomeViewModel()
        //  Class Constructor
        public HomeViewModel()
        {
            this.RefreshUsers();
            userListClick = new RelayCommand<SelectionChangedEventArgs>(_userListClick);
            saveUser = new RelayCommand(saveUser);
        }

        // users 
        //  list of all users in the system
        private IEnumerable<user> _users;
        public IEnumerable<user> users
        {
            get { return this._users; }
            set
            {
                this._users = value;
                this.RaisePropertyChanged("users");
            }

        }

        // selected user
        //  currently selected user
        private user _selectedUser;
        public user selectedUser
        {
            get { return this._selectedUser; }
            set
            {
                if (this._selectedUser != value)
                {
                    this._selectedUser = value;
                    this.RaisePropertyChanged("selectedUser");
                }
            }
        }

        // RefreshUsers() 
        //  Method for retrieving users from DB
        private void RefreshUsers()
        {
            this._pkServiceClient.getUsersCompleted += (s, e) =>
                {
                    this.users = e.Result;
                };
            this._pkServiceClient.getUsersAsync();

        }

        // serverListClick
        public ICommand userListClick { get; private set; }
        private void _userListClick(SelectionChangedEventArgs e)
        {
            ListView userList = (ListView)e.Source;
            _selectedUser = (user)userList.SelectedItem;
            this.setUserDetailsVisible(Visibility.Visible);
            this.RaisePropertyChanged("selectedUser");
        }        

        public ICommand saveUser { get; private set; }
        private void _saveUser()
        {
            this._pkServiceClient.updateUser(_selectedUser);
        }
    }
}

I believe it is possible to tie RelayCommand(saveUser, someCommand) and have someCommand return a value that I can bind to in my XAML to control the enabled state of my button.

I cannot seem to find a way using the Entity Generated class to handle this. any assistance would be wonderful.

1

1 Answers

1
votes

If I understand you correctly, you want to enable the Button connected to the saveUser Command as soon, as there are changes made to the selectedUser?

I believe it is possible to tie RelayCommand(saveUser, someCommand) and have someCommand return a value that I can bind to in my XAML to control the enabled state of my button.

This is possible with CompositeCommands (MSDN Documentation). Here you can call the method "RegisterCommand" and pass a second command which affects the behavior of the Button connected to the CompositeCommand. So if the registered Button returns CanExecute == false, the button will be disabled.

But you could also just add an CanExecute method to the RelayCommand and check there, if the entity has a correct value. For example, a CanExecute method could check for example the userName:

public HomeViewModel()
{
    this.RefreshUsers();
    userListClick = new RelayCommand<SelectionChangedEventArgs>(_userListClick);
    saveUser = new RelayCommand(saveUser, CanExecuteSaveUser);
}

private bool CanExecuteSaveUser()
    {
        if (selectedUser.username == "foo")
        {
            return true;
        }

        return false;
    }

I would not connect this directly to your model. Your model should not be bothered with logic needed for validation of the UI. So you should probably add an additional layer between ViewModel and the WCF Service, kind of a UserViewModel where you can handle all changes made to properties of the User directly. Changes in these Properties can trigger RaiseCanExecuteChanged method of RelayCommand.

I hope this helps, if i misunderstood you, please provide further explanation of the problem.

Edit:

After your comment, I think you can implement the INotifyPropertyChanged event and subscribe to these events. For this, it would be necessary to add an additional layer of userViewModel to the application. Very stripped example:

public HomeViewModel()
{
    this.RefreshUsers();
    userListClick = new RelayCommand<SelectionChangedEventArgs>(_userListClick);
    saveUser = new RelayCommand(saveUser, CanExecuteSaveUser);
    selectedUserViewModel.PropertyChanged += SelectedUserOnPropertyChanged;
}

private void SelectedUserOnPropertyChanged(object sender, PropertyChangedEventArgs propertyChangedEventArgs)
{
    if (propertyChangedEventArgs.PropertyName.Equals(nameof(UserViewModel.username)))
    {
        (saveUser as RelayCommand)?.RaiseCanExecuteChanged();
    }
}