WPF Commanding: Enable Disable Button with Command Property

Posted by: Mahesh Sabnis , on 5/13/2015, in Category WPF
Views: 74008
Abstract: Walkthrough for using WPF commanding for enabling / disabling Button with Command Property

Commanding is a very useful feature in XAML based technologies (WPF, Windows Phone, Windows Store Apps, Silverlight). The command property is available for action based elements for e.g. Button, MenuItem, etc.

The Prism library provides you with the DelegateCommand types which is used to handle click events on the Button element and can execute remote functionality without explicitly subscribing to the ‘click’ event of the element. There are various article published on commanding for various XAML based technologies which can be found for Silverlight, WPF and Windows Phone.

 

I was recently asked a question on how to disable a button (which is set to the command property) when the View is loaded; and then enable it again when the end-user starts entering data in some textboxes. For e.g. in a WPF Search application, only if the end-user enters some text in the Search textbox, the Search button should become enabled. If the text from the textbox is removed then the button should be disabled again. Let us see the solution to this challenge.

Step 1: Open Visual Studio Community Edition (you can use any version of Visual Studio 2012/2013/2015 along with WPF 4.5) and create a new WPF Project. Name it as ‘WPF45_Commanding_Enable_Disable’. In this project add three folders of the name ModelClasses, Commands and ViewModels.

Step 2: In the ModelClasses folder, add a new class file and add the following classes in it:

using System.Collections.ObjectModel;

namespace WPF45_Commanding_Enable_Disable.ModelClasses
{
    /// <summary>
    /// The Class defining Properties for PersonInfo
    /// </summary>
    public class PersonInfo
    {
        public int PersonId { get; set; }
        public string FirstName { get; set; }
        public string  LastName { get; set; }

        public string City { get; set; }
        public int Age { get; set; }
    }

    /// <summary>
    /// The Person DataStore class
    /// </summary>
    public class PersonsDataStore : ObservableCollection<PersonInfo>
    {
        public PersonsDataStore()
        {
            Add(new PersonInfo() {PersonId=1,FirstName="Tejas", LastName="Sabnis",Age=10,City="Pune" });
            Add(new PersonInfo() { PersonId = 2, FirstName = "Mahesh", LastName = "Sabnis", Age = 38, City = "Pune" });
            Add(new PersonInfo() { PersonId = 3, FirstName = "Ramesh", LastName = "Sabnis", Age = 73, City = "Yavatmal" });
            Add(new PersonInfo() { PersonId = 4, FirstName = "Aditya", LastName = "Bhagat", Age = 3, City = "Pune" });
            Add(new PersonInfo() { PersonId = 5, FirstName = "Prashant", LastName = "Bhagat", Age = 31, City = "Pune" });
            Add(new PersonInfo() { PersonId = 6, FirstName = "Tejas", LastName = "Kumar", Age = 26, City = "Delhi" });
            Add(new PersonInfo() { PersonId = 7, FirstName = "Aditya", LastName = "Kumar", Age = 25, City = "Delhi" });
            Add(new PersonInfo() { PersonId = 8, FirstName = "Sanjay", LastName = "Kulkarni", Age = 55, City = "Nagpur" });
            Add(new PersonInfo() { PersonId = 9, FirstName = "Abhay", LastName = "Kulkarni", Age = 51, City = "Akola" });
            Add(new PersonInfo() { PersonId = 10, FirstName = "Anil", LastName = "Deshpande", Age = 45, City = "Pune" });
            Add(new PersonInfo() { PersonId = 11, FirstName = "Anil", LastName = "Kulkarni", Age = 50, City = "Yavatmal" });
        }
    }
    /// <summary>
    /// The DataAccess class
    /// </summary>
    public class DataAccess
    { 
        public ObservableCollection<PersonInfo> GetPersonData()
        {
            return new PersonsDataStore();
        }
    }
}


The above code contains classes for defining Person information, Person Data Store and the Person Data Access.

Step 3: In the Commands folder, add a new class file with the following code:

using System;
using System.Windows.Input;

namespace WPF45_Commanding_Enable_Disable.Commands
{
    /// <summary>
    /// The Command class
    /// </summary>
    public class RelayActionCommand : ICommand
    {
        /// <summary>
        /// The Action Delegate representing a method with input parameter 
        /// </summary>
        public Action<object> ExecuteAction { get; set; }
        /// <summary>
        /// The Delegate, used to represent the method which defines criteria for the execution 
        /// </summary>
        public Predicate<object> CanExecuteAction { get; set; }

        public bool CanExecute(object parameter)
        {
            if (CanExecuteAction != null)
            {
                return CanExecuteAction(parameter);
            }
            return true;
        }

        /// <summary>
        /// The event which will be raised based upon the
        /// value set for the command.
        /// </summary>
        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }

        public void Execute(object parameter)
        {
            if (ExecuteAction != null)
            {
                ExecuteAction(parameter);
            }
        }
    }
}

The above command class defines two delegates of type Action<T> and Predicate<T>. The action delegate represents the method having ‘T’ as its input parameter. The predicate delegate represent the method which defines criteria for the execution.

The CanExecuteChanged event is used to raise the Command event. This uses CommandManager class to provide command related utility methods used to register CommandBinding and InputBinding objects for Commands. This also helps to add or remove the commands and provides querying feature for the commands.

The CommandManager provides RequerySuggested event. This event occurs when the CommandManager detects the conditions that changes the ability of command to execute.

In our example, when the Search TextBox contains some data, the Commanding is enabled, else it is disabled.

Step 4: In the ViewModels folder, add a new class file with the following code in it:

using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows.Data;
using WPF45_Commanding_Enable_Disable.Commands;
using WPF45_Commanding_Enable_Disable.ModelClasses;

namespace WPF45_Commanding_Enable_Disable.ViewModels
{
    /// <summary>
    /// The ViewModel class
    /// </summary>
    public class PersonViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

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

        ObservableCollection<PersonInfo> _Persons;

        public ObservableCollection<PersonInfo> Persons
        {
            get { return _Persons; }
            set { _Persons = value; }
        }


        string _Name;

        public string Name
        {
            get { return _Name; }
            set 
            { 
                _Name = value;
                OnPropertyChanged("Name");
            }
        }

         

        public RelayActionCommand SearchPersonCommnad { get; set; }

        DataAccess objds;
        public PersonViewModel()
        {
            Persons = new ObservableCollection<PersonInfo>();
            objds = new DataAccess();


            Persons = new ObservableCollection<PersonInfo>(objds.GetPersonData());

            var defaultView = CollectionViewSource.GetDefaultView(Persons);

            //The Command object is initialized where the 'CanExecuteAction' will be set True or False
            //based upon the State of the 'Name' property
            //The 'ExecuteAction' will accepts the data filtered from the collection
            //based upon the data entered in the TextBox
            SearchPersonCommnad = new RelayActionCommand()
            {
                CanExecuteAction = n=> !String.IsNullOrEmpty(Name),
                 ExecuteAction = n => defaultView.Filter = name => ((PersonInfo)name).FirstName.StartsWith(Name) 
                     || ((PersonInfo)name).LastName.StartsWith(Name) 
                     || ((PersonInfo)name).City==Name
            };
        }

    }
}

The PersonViewModel class defines Persons of type ObservableCollection<T> where T is of the type Person. The property ‘Name’ is used to bind with the TextBox in View. The Name property is used for filtering the data from the Persons collection. The constructor gets the Person Collection from the data access class.

A major portion of the code is the declaration of the SearchPersonCommand command object. This is initialized with the delegate properties set using CanExecuteAction and ExecuteAction. The CanExecuteAction represents a method which will be either True or False based upon the state of the TextBox on View. The ExecuteAction represent a method that will be a criteria for filtering data from Person Collection based upon the text entered in the TextBox.

Step 5: Add the following XAML in MainWindow.xaml

<Window x:Class="WPF45_Commanding_Enable_Disable.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:vm="clr-namespace:WPF45_Commanding_Enable_Disable.ViewModels"
        Title="MainWindow" Height="536.842" Width="744.737">
    <Window.Resources>
        <vm:PersonViewModel x:Key="personvm"></vm:PersonViewModel>
    </Window.Resources>
    <Grid DataContext="{Binding Source={StaticResource personvm}}">
        <Grid.RowDefinitions>
            <RowDefinition Height="87*"/>
            <RowDefinition Height="111*"/>
            <RowDefinition Height="308*"/>
        </Grid.RowDefinitions>
        
        <TextBlock TextWrapping="Wrap" Text="Enabling Disabling Command Button Based Upon the Text an TextBoxes"
                    FontSize="30" TextAlignment="Center" FontWeight="ExtraBold"/>
        <TextBlock HorizontalAlignment="Left" Height="39" 
                   Margin="10,10,0,0" Grid.Row="1" TextWrapping="Wrap" Text="Enter First Name or Last Name or City:" 
                   VerticalAlignment="Top" Width="417" FontSize="20" FontWeight="ExtraBold"/>

        <TextBox HorizontalAlignment="Left" Height="39" Margin="456,12,0,0"
                 Grid.Row="1" TextWrapping="Wrap" VerticalAlignment="Top"
                 Width="177" Name="txtfname"
                  Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}"/>
        
        <DataGrid Grid.Row="2" Name="dgPersons" ItemsSource="{Binding Persons}" ColumnWidth="*"/>
        
        <Button Content="Get Persons" HorizontalAlignment="Left"  Name="btngetpersons" FontSize="20"
                FontWeight="ExtraBold" Command="{Binding SearchPersonCommnad}"
                Margin="10,54,0,0" Grid.Row="1" VerticalAlignment="Top" Width="717" Height="47"/>

    </Grid>
</Window>

This XAML defines an instance of the PersonViewModel class. The textbox is bound with the Name property from the ViewModel class. A point to note here is the ‘UpdateSourceTrigger’ property of the Binding class which is set to the ‘PropertyChanged’ event. This means that when the text is entered in the textbox, the CanExecuteAction will be true and the Button, which is bound with the SearchPersonCommand property from the PersonViewModel class, will be enabled. When the text from the textbox is removed then the button will be disabled.

Step 6: Run the application and you should see the following result with ‘Get Persons’ button as disabled:

wpf-commanding

Enter some text in the TextBox and the Get Person button will be enabled. Click on the button and the result will be as shown here:

wpf-enable-disable

Conclusion: Commanding in WPF and similar XAML technologies can be controlled using CommandManager and its RequerySuggested event which helps to manage the UI Elements’ behaviour.

Download the entire source code of this article (Github)

This article has been editorially reviewed by Suprotim Agarwal.

Absolutely Awesome Book on C# and .NET

C# and .NET have been around for a very long time, but their constant growth means there’s always more to learn.

We at DotNetCurry are very excited to announce The Absolutely Awesome Book on C# and .NET. This is a 500 pages concise technical eBook available in PDF, ePub (iPad), and Mobi (Kindle).

Organized around concepts, this Book aims to provide a concise, yet solid foundation in C# and .NET, covering C# 6.0, C# 7.0 and .NET Core, with chapters on the latest .NET Core 3.0, .NET Standard and C# 8.0 (final release) too. Use these concepts to deepen your existing knowledge of C# and .NET, to have a solid grasp of the latest in C# and .NET OR to crack your next .NET Interview.

Click here to Explore the Table of Contents or Download Sample Chapters!

What Others Are Reading!
Was this article worth reading? Share it with fellow developers too. Thanks!
Share on LinkedIn
Share on Google+

Author
Mahesh Sabnis is a DotNetCurry author and a Microsoft MVP having over two decades of experience in IT education and development. He is a Microsoft Certified Trainer (MCT) since 2005 and has conducted various Corporate Training programs for .NET Technologies (all versions), and Front-end technologies like Angular and React. Follow him on twitter @maheshdotnet or connect with him on LinkedIn


Page copy protected against web site content infringement 	by Copyscape




Feedback - Leave us some adulation, criticism and everything in between!
Comment posted by Ken G on Friday, May 15, 2015 12:59 AM
This is a very helpful article which I will use in one of my WPF projects right away. Thanks for sharing
Comment posted by Mahesh sabnis on Sunday, May 24, 2015 10:40 AM
Hi Ken G,

  Thanks a lot.
Regards
Mahesh Sabnis