DEV Community

Brandon Weaver
Brandon Weaver

Posted on

Responsive User Interface

In this part of the series, we'll implement some logic to create a responsive user interface. We'll begin by adding a notification base class, which will allow us to inherit a property changed event and callback in our view-model. We may move the notification base class into the models folder next week when we look at editing existing entries. For now, simply create a new class in the 'ViewModels' folder. Name the class 'NotificationBase.cs', and add the following code.

using System.ComponentModel;

namespace MVVMIntroUI.ViewModels
{
    class NotificationBase : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged(string propertyName)
        {
            this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

The next thing we will create is a command system, which will allow us to respond to UI events from within our view-model. Again, create a new class in the 'ViewModels' folder. This time, we'll simply name the class 'Command.cs', and implement the 'ICommand' interface. We'll add an 'Action' field to the class, which will allow us to store a callback in each instance of our command class.

using System;
using System.Windows.Input;

namespace MVVMIntroUI.ViewModels
{
    class Command : ICommand
    {
        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }

        private readonly Action _function;

        public Command(Action function)
        {
            this._function = function;
        }

        public bool CanExecute(object parameter)
        {
            return true;
        }

        public void Execute(object parameter)
        {
            this._function.Invoke();
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Now, we need to completely overhaul our people view-model, and add properties and methods, which we will bind to our people view. Open 'PeopleViewModel.cs' and make the following changes.

using MVVMIntroUI.Models;
using System.Collections.ObjectModel;

namespace MVVMIntroUI.ViewModels
{
    class PeopleViewModel : NotificationBase
    {
        public ObservableCollection<Person> People { get; set; }

        private string _firstNameTextBox;

        public string FirstNameTextBox
        {
            get { return this._firstNameTextBox; }
            set
            {
                this._firstNameTextBox = value;
                this.OnPropertyChanged(nameof(this.FirstNameTextBox));
            }
        }

        private string _lastNameTextBox;

        public string LastNameTextBox
        {
            get { return this._lastNameTextBox; }
            set
            {
                this._lastNameTextBox = value;
                this.OnPropertyChanged(nameof(this.LastNameTextBox));
            }
        }

        private string _emailTextBox;

        public string EmailTextBox
        {
            get { return this._emailTextBox; }
            set
            {
                this._emailTextBox = value;
                this.OnPropertyChanged(nameof(this.EmailTextBox));
            }
        }

        public Command OnSubmitCommand { get; set; }

        public PeopleViewModel()
        {
            this.People = new ObservableCollection<Person>();
            this.OnSubmitCommand = new Command(this.OnSubmit);
        }

        public void OnSubmit()
        {
            this.People.Add(
                new Person
                (
                this.FirstNameTextBox,
                this.LastNameTextBox,
                this.EmailTextBox
                )
            );
            this.FirstNameTextBox = string.Empty;
            this.LastNameTextBox = string.Empty;
            this.EmailTextBox = string.Empty;
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

You'll notice that we're calling 'OnPropertyChanged' from within the setter method of each property we need to track, and passing in the name of the property. We're also initializing the class with a command and passing in the 'OnSubmit' method. We've changed our people collection from a 'List' to an 'ObservableCollection'. This collection will raise a property changed event when a change is made to the collection.

We're now ready to create the necessary UI elements in our people view, and bind our properties and commands. Open 'PeopleView.xaml', and make the following changes. Feel free to arrange the elements however you would like. Just be sure to include the text boxes and button necessary for adding a new entry.

<UserControl x:Class="MVVMIntroUI.Views.PeopleView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:vm="clr-namespace:MVVMIntroUI.ViewModels"
             mc:Ignorable="d"
             d:DesignHeight="450" d:DesignWidth="800">
    <UserControl.Resources>
        <vm:PeopleViewModel x:Key="PeopleViewModel" />
    </UserControl.Resources>
    <Grid DataContext="{StaticResource PeopleViewModel}"
          Margin="5">
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="auto" />
        </Grid.RowDefinitions>
        <DataGrid Grid.Row="0"
                  ItemsSource="{Binding People}" />
        <Grid Grid.Row="1">
            <Grid.RowDefinitions>
                <RowDefinition Height="auto" />
                <RowDefinition Height="auto" />
                <RowDefinition Height="auto" />
                <RowDefinition Height="auto" />
            </Grid.RowDefinitions>
            <TextBox Grid.Row="0"
                     Text="{Binding FirstNameTextBox, Mode=TwoWay}" />
            <TextBox Grid.Row="1"
                     Text="{Binding LastNameTextBox, Mode=TwoWay}" />
            <TextBox Grid.Row="2"
                     Text="{Binding EmailTextBox, Mode=TwoWay}" />
            <Button Grid.Row="3"
                    Content="Submit"
                    Command="{Binding OnSubmitCommand}" />
        </Grid>
    </Grid>
</UserControl>
Enter fullscreen mode Exit fullscreen mode

Take note of the two-way binding. This allows us to control the properties of our view-model from the view, and control the content of each element from within the view-model. This way, we're able to clear each text box from within the 'OnSubmit' method of our people view-model.

In the following part of this series, we'll look into data persistence, and add some logic to manipulate existing entries.

Oldest comments (0)