Friday 9 December 2016

Create a Sample MVVM Application in WPF with ICommand , INotificationChanged,ObservableCollection Implementation


In this post we are going to see how to create Sample MVVM Application in WPF with ICommand , INotificationChanged, ObservationCollection Implementation For this first we have to create a class which implements the ICommand, name it as Relaycommand, then Create a ViewModelBase class , Then derive it in ViewModels




    public class RelayCommand : ICommand
    {
        Action _executeMethod;
        Func<bool> _canexecuteMethod;

        public RelayCommand(Action executeMethod)
        {
            _executeMethod = executeMethod;
        }

        public RelayCommand(Action executeMethod, Func<bool> canExecuteMethod)
        {
            _executeMethod = executeMethod;
            _canexecuteMethod = canExecuteMethod;
        }

        public void RaiseCanExecuteChanged()
        {
            CanExecuteChanged(this, EventArgs.Empty);
        }


        #region ICommand Members

        bool ICommand.CanExecute(object param)
        {
            if (_canexecuteMethod != null)
            {
                return _canexecuteMethod();
            }
            if (_executeMethod != null)
            {
                return true;
            }
            return false;
        }

       public event EventHandler CanExecuteChanged = delegate { };

        void ICommand.Execute(object param)
        {
            _executeMethod?.Invoke();
        }
        #endregion
    }



    public class RelayCommand<T> : ICommand
    {
        Action<T> _executeMethod;
        Func<T, bool> _canexecuteMethod;

        public RelayCommand(Action<T> executeMethod)
        {
            _executeMethod = executeMethod;
        }

        public RelayCommand(Action<T> executeMethod, Func<T,bool> canExecuteMethod)
        {
            _executeMethod = executeMethod;
            _canexecuteMethod = canExecuteMethod;
        }

        public void RaiseCanExecuteChanged()
        {
             CanExecuteChanged(this, EventArgs.Empty);
        }
        #region ICommand Members

        bool ICommand.CanExecute(object param)
        {
            if (_canexecuteMethod != null)
            {
                T typparm = (T)param;
                return _canexecuteMethod(typparm);
            }
            if (_executeMethod != null)
            {
                return true;
            }
            return false;
        }

      
        public event EventHandler CanExecuteChanged = delegate { };

        void ICommand.Execute(object param)
        {
            _executeMethod?.Invoke((T)param);
        }

        #endregion

    }




Model:
*************

public class Employee
    {
        public Employee()
        {
            
        }
        [Key]
        public Guid Id { getset; }
        public string FirstName { getset; }
        public string LastName { getset; }
        public string FullName { get { return FirstName + " " + LastName; } }
        public string Phone { getset; }
        public string Email { getset; }
        public string Street { getset; }
        public string City { getset; }
        public string State { getset; }
        public string Zip { getset; }
            
}


    public class ViewModelBase : INotifyPropertyChanged
    {
        protected virtual void Set<T>(ref T member, T val,
            [CallerMemberNamestring propertyName = null)
        {
            if (object.Equals(member, val)) return;

            member = val;
            PropertyChanged(thisnew PropertyChangedEventArgs(propertyName));
        }

        protected virtual void OnPropertyChanged(string propertyName)
        {
            PropertyChanged(thisnew PropertyChangedEventArgs(propertyName));
        }
        public event PropertyChangedEventHandler PropertyChanged = delegate { };

    }




From the above code you may notice that one command have input paramter , another one not, so let us see how to use this relay command , first we have to create the instance of the Relaycommand like below




EmployeeListViewModel.cs
*******************************'

    class EmployeeListViewModel : ViewModelBase
    {

   public RelayCommand AddEmployeeCommand { get; private set; }

   public RelayCommand<Employee> EditEmployeeCommand { get; private set; }

   private ObservableCollection<Employee> _employees;

          public ObservableCollection<Employee> Employees
          {
            get { return _employees; }
            set {
                Set<ObservableCollection<Employee>>(ref _employees, value);
            }
          }

         public EmployeeListViewModel()
         {
            AddEmployeeCommand = new RelayCommand(OnAddEmployee);      
            EditEmployeeCommand = new RelayCommand<Employee>(onEditEmployee);
         }

                public async void LoadEmployees()
         {
          Employees = new ObservableCollection<Employee>(await _Service.GetdatasAsync());
         }

        private void onEditEmployee(Employee obj)
        {
            EditEmployeeRequested(obj);
        }

        private void OnAddEmployee()
        {
            AddEmployeeRequested(new Employee() { Id = Guid.NewGuid() });
        }

}



Then we have to bind that command with the View like below.


EmployeeListView.xaml
*******************************'


<UserControl
             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:local="clr-namespace:Sample.Employees"
             xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
             xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
             x:Class="Sample.Employees.EmployeeListView"
             mc:Ignorable="d"
             d:DesignHeight="300" d:DesignWidth="300">


    <i:Interaction.Triggers>
        <i:EventTrigger EventName="Loaded">
            <ei:CallMethodAction TargetObject="{Binding}" MethodName="LoadEmployees"/>
        </i:EventTrigger>
    </i:Interaction.Triggers>

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <Grid Grid.Row="0">
            <Button Content="Add Employee"
                    Command="{Binding AddEmployeeCommand}"
                    HorizontalAlignment="Left"
                    ></Button>
        </Grid>

        <DataGrid ItemsSource="{Binding Employees}" Grid.Row="1"
                     AutoGenerateColumns="False"
                     CanUserAddRows="False"
                     CanUserDeleteRows="False"
                  >
            <DataGrid.Columns>

                <DataGridTextColumn Binding="{Binding FullName}" Width="*"/>
               

                <DataGridTemplateColumn Width="Auto">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <Button Content="Edit"
                                    Command="{Binding DataContext.EditEmployeeCommand,
                        RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}"
                                    CommandParameter="{Binding}"
                                    Margin="5"
                                    ></Button>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>

            </DataGrid.Columns>
        </DataGrid>
    </Grid>
</UserControl>




This follows the DataTemplate model to place the view, see the main view code , how the template is binding with View


Here in the main view you can see that the View is binded against the model, when ever the model is loaded , corresponding view will be bind using DataTemplate, in DataTemplate we have to say , for this viewmodel, this view must load, For this to work we have to use the ContentControl, where we have to use the CurrentViewmodel property to change the view





MainWindow.xaml
**********************

<Window x:Class="Sample.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:Employees="clr-namespace:Sample.Employees"
        xmlns:local="clr-namespace:Sample"
        Title="MainWindow" Height="300" Width="300">

    <Window.DataContext>
        <local:MainWindowViewModel />
    </Window.DataContext>

    <Window.Resources>
        <DataTemplate DataType="{x:Type Employees:EmployeeListViewModel}">
            <Employees:EmployeeListView />
        </DataTemplate>
        <DataTemplate DataType="{x:Type Employees:EmployeeViewModel}">
            <Employees:AddEditEmployeeView />
        </DataTemplate>
    </Window.Resources>

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>

        <Grid Grid.Row="0"  x:Name="NavBar">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>
              <Button Command="{Binding NavCommand}" CommandParameter="Employees"
Grid.Column="0" Content="Employee List" />
<Button Command="{Binding NavCommand}" CommandParameter="EditEmp"
Grid.Column="2" Content="Edit Employees" />
        </Grid>
       
        <Grid Grid.Row="1" x:Name="MainContent">
            <ContentControl Content="{Binding CurrentViewModel}" />
        </Grid>
    </Grid>
</Window>


Here in MainViewModel you can see that the Navigation is done by CurrentViewModel



MainWindowViewModel
**********************

   class MainWindowViewModel:ViewModelBase
    {

        private EmployeeListViewModel _empListModel = new EmployeeListViewModel();
        private EmpViewModel _addEditempModel = new EmpViewModel();

        private ViewModelBase _currentViewModel;

        public MainWindowViewModel()
        {
            NavCommand = new RelayCommand<string>(OnNav);
            _empListModel.AddEmployeeRequested += NavToAddEmployee;
            _empListModel.EditEmployeeRequested += NavToEditEmployee;
        }

        private void NavToEditEmployee(Employee obj)
        {
            _addEditempModel.EditMode = true;
            _addEditempModel.SetEmployee(obj);
            CurrentViewModel = _addEditempModel;
        }

        private void NavToAddEmployee(Employee obj)
        {
            _addEditempModel.EditMode = false;
            _addEditempModel.SetEmployee(obj);
            CurrentViewModel = _addEditempModel;
        }

        private void NavToPlaceOrder(Guid EmployeeId)
        {
            _ordModel.EmployeeId = EmployeeId;
            CurrentViewModel = _ordModel;
        }

        public ViewModelBase CurrentViewModel
        {
            get { return _currentViewModel; }
            set { Set<ViewModelBase>(ref _currentViewModel, value); }
        }

        public RelayCommand<string> NavCommand { get; private set; }

        private void OnNav(string viewname)
        {
            switch (viewname)
            {
                case "EditEmp":
                    CurrentViewModel = _ addEditempModel;
                    break;
                case "Employees":
                default:
                    CurrentViewModel = _empListModel;
                    break;
            }
        }

    }


ManiViewModel is  binded in DataContext of the View so when MainViewModel Loadeds it will bind everything, Based on the Button click , Corresponding ViewModel instance is created and Bind to the CurrentViewModel, when this Viewmodel loades the Corresponding View get load using the ContentControl and DataTemplate





From this post you can learn how to bind the Commands in the WPF.

No comments:

Post a Comment