Tag Archives: RelayCommand

An ADR Visual Studio Tool – Part 2 – Refactoring

A short while ago, I wrote an article about how to create a new extension for Visual Studio. The end target of this is to have a tool that will allow easy management of ADR Records.

In this post, I’m going to clean up some of the code in that initial sample. There’s nothing new here, just some basic WPF good practices. If you’re interested in downloading, or just seeing the code, it’s here.

What’s wrong with what was there?

The extension worked (at least as far as it went), but it used code behind to execute the functionality. This means that the logic and the UI are tightly coupled. My guess is that soon (maybe as part of .Net 5) the extensions will move over to another front end tech (i.e. not WPF), which means that people that have written extensions may need to re-write them. This is a guess – I don’t know any more than you do.

Onto the refactoring… Starting with MVVM basics

Let’s start with a simple View Model; previously, the code was in the code behind, so we’ll move that all over to a view model:

    public class AdrControlViewModel : INotifyPropertyChanged
    {
        public AdrControlViewModel()
        {
            Scan = new RelayCommandAsync<object>(ScanCommand);
        }

        private string _summary;
        public string Summary 
        { 
            get => _summary; 
            set => UpdateField(ref _summary, value); 
        }

        public RelayCommandAsync<object> Scan { get; set; }

        private async Task ScanCommand(object arg)
        {
            var solutionAnalyser = new SolutionAnalyser();
            Summary = await solutionAnalyser.ScanSolution();            
        }
    }

You’ll also need the following INotifyPropertyChanged boilerplate code:

        #region INotifyPropertyChanged
        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged([CallerMemberName]string fieldName = null) =>        
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(fieldName));

        private void UpdateField<T>(ref T field, T value, [CallerMemberName]string fieldName = null)
        {
            field = value;
            OnPropertyChanged(fieldName);
        }
        #endregion

One day, this can go into a base class, if we ever create a second View Model. We’ll come back to SolutionAnalyser in a while. I shamelessly pilfered the RelayCommand code from here. Finally, I did a bit of shuffling around:

Finally, the code behind needs to be changed as follows:

    public partial class AdrControl : UserControl
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="AdrControl"/> class.
        /// </summary>
        public AdrControl()
        {
            this.InitializeComponent();
            DataContext = new AdrControlViewModel();
        }   
    }

SolutionAnalyser

This is, essentially, the only real code that actually does anything. It’s likely to be severely refactored in a later incarnation, but for now, it’s just in its own class:

    public class SolutionAnalyser
    {
        internal async Task<string> ScanSolution()
        {
            try
            {
                await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
                var dte = (DTE)Package.GetGlobalService(typeof(DTE));

                var sln = Microsoft.Build.Construction.SolutionFile.Parse(dte.Solution.FullName);
                string summaryText = $"{sln.ProjectsInOrder.Count.ToString()} projects";

                foreach (Project p in dte.Solution.Projects)
                {
                    summaryText += $"{Environment.NewLine} {p.Name} {p.ProjectItems.Count}";
                }
                return summaryText;
            }
            catch
            {
                return "Solution is not ready yet.";
            }            
        }
    }

What’s next?

The next stage is to introduce a search and create facility. I’m going to start creating some issues in the GitHub repo when I get some time.

Unit Testing an MVVM RelayCommand

The RelayCommand is an excellent way to communicate between a view and View Model in MVVM without creating a link to a specific instance of either. However; one problem that I recently encountered when using these was: how are they unit tested.

Take the following piece of code (taken from https://tfsutilities.codeplex.com/):

    public class MainViewModel : ViewModelBase
    {          
        public RelayCommand FindOrphanedWorkspaceCommand { get ; private set ; }

This is instantiated here:

         public MainViewModel( IDataService dataService)
        {
            ...

            FindOrphanedWorkspaceCommand = new RelayCommand(FindOrphanedWorkspaces);

And, finally, the method itself:

         /// <summary>
        /// Call method to retrieve all workspaces that currently have no pending changes
        /// </summary>
        private void FindOrphanedWorkspaces()
        {
            // Doesn't really matter what this actually does

So; can we just write a test; what about this:

    [TestClass]
    public class MainViewModelTest
    {
        [ TestMethod]
        public void FindOrphanedWorkspaces()
        {
            IDataService ds = new Mock.MockDataService ();
            MockMainViewModel mvm = new MockMainViewModel (ds);

            mvm.FindOrphanedWorkspaceCommand();

        }

The answer, of course, is no (otherwise this wouldn’t be a particularly useful blog post).

The Workaround

There is a workaround (which is pretty much by fallback workaround these days when I find something can’t be unit tested for reasons of protection):

Simply change the private method to protected and subclass the viewmodel:

    class MockMainViewModel : MainViewModel
    {
        public MockMainViewModel( IDataService dataService) : base (dataService) { }

         public void FindOrphanedWorkspaces()
        {
            base.FindOrphanedWorkspaces();

This certainly works and, in its defence, it tests as completely as calling the RelayCommand (I don’t believe testing the MVVM architecture is within the remit of the test architecture of any dependent program).

However…

It feels like a lot of additional work (as it happens, it’s work you may have to do anyway for other things, but that’s beside the point). So, what’s the alternative?

RelayCommand implements ICommand, here’s the metadata:

namespace GalaSoft.MvvmLight.Command
{
    // Summary:
    //     A command whose sole purpose is to relay its functionality to other objects
    //     by invoking delegates. The default return value for the CanExecute method
    //     is 'true'. This class does not allow you to accept command parameters in
    //     the Execute and CanExecute callback methods.
    public class RelayCommand : ICommand
    {
        // Summary:
        //     Initializes a new instance of the RelayCommand class that can always execute.
        //
        // Parameters:
        //   execute:
        //     The execution logic.
        //
        // Exceptions:
        //   System.ArgumentNullException:
        //     If the execute argument is null.
        public RelayCommand( Action execute);
        //
        // Summary:
        //     Initializes a new instance of the RelayCommand class.
        //
        // Parameters:
        //   execute:
        //     The execution logic.
        //
        //   canExecute:
        //     The execution status logic.
        //
        // Exceptions:
        //   System.ArgumentNullException:
        //     If the execute argument is null.
        public RelayCommand( Action execute, Func< bool> canExecute);

        // Summary:
        //     Occurs when changes occur that affect whether the command should execute.
        public event EventHandler CanExecuteChanged;

        // Summary:
        //     Defines the method that determines whether the command can execute in its
        //     current state.
        //
        // Parameters:
        //   parameter:
        //     This parameter will always be ignored.
        //
        // Returns:
        //     true if this command can be executed; otherwise, false.
        public bool CanExecute( object parameter);
        //
        // Summary:
        //     Defines the method to be called when the command is invoked.
        //
        // Parameters:
        //   parameter:
        //     This parameter will always be ignored.
        public virtual void Execute( object parameter);
        //
        // Summary:
        //     Raises the GalaSoft.MvvmLight.Command.RelayCommand.CanExecuteChanged event.
        public void RaiseCanExecuteChanged();
    }
}

Consequently, you can simply do this:

        [ TestMethod]
        public void FindOrphanedWorkspaces()
        {
            IDataService ds = new Mock.MockDataService ();
            MockMainViewModel mvm = new MockMainViewModel (ds);

            mvm.FindOrphanedWorkspaceCommand.Execute( null);

        }

And, there’s more. If you’ve implemented the CanExecute, you can test if that works; for example:

Assert.IsTrue(mvm.FindOrphanedWorkspaceCommand.CanExecute(null));

Or

Assert.IsFalse(mvm.FindOrphanedWorkspaceCommand.CanExecute(null));

In my case, as it stands, the former.

Conclusion

So, we can check whether the command can execute, and whether it does execute. Admittedly this isn’t ground-breaking research, but it took me longer that it should have to figure it out, and next time, I’ll have a blog post to refer to.