WPF 4: Using Input Bindings to Go Mouseless

While creating the UI of an application, we need to adopt techniques that makes the user comfortable with the app. If the existing user is habitual of using shortcut keys for various operations, then while developing the new user interface, it is necessary for the UI developer to provide a similar functionality. In WPF, we have such a facility using ‘InputBindings’. Using this feature, operational shortcut facility can be provided. InputBindings in WPF can be provided on any element. The InputBinding contains KeyBinding which this is used to configure Command based upon keyboard keys and modifiers e.g. Control etc. and also MouseBinding for mouse actions. Typically this approach is used for MVVM based applications.

For this application have used the following Table:

image

Step 1: Open VS2010 and create a WPF Application and name it as ‘WPF4_InptuBinding’. To this project, add a class file and call it ‘DataClasses.cs’. Add the following code to it:

using System;
using System.Collections.ObjectModel;
using System.Data.SqlClient;

namespace WPF4_InptuBinding
{
    public class EmployeeInfo
    {
        public int EmpNo { get; set; }
        public string EmpName { get; set; }
        public int Salary { get; set; }
        public string DeptName { get; set; }
        public string Designation { get; set; }
    }

    public class EmployeeInfoDAL
    {
        SqlConnection Conn;
        SqlCommand Cmd;

        public EmployeeInfoDAL()
        {
            Conn = new SqlConnection("Data Source=.;" +
                "Initial Catalog=Company;Integrated Security=SSPI");
        }
        public ObservableCollection<EmployeeInfo> GetEmployees()
        {
            ObservableCollection<EmployeeInfo> EmpCollection =
                new ObservableCollection<EmployeeInfo>();
            Conn.Open();
            Cmd = new SqlCommand();
            Cmd.Connection = Conn;
            Cmd.CommandText = "Select * from EmployeeInfo";
            SqlDataReader Reader = Cmd.ExecuteReader();
            while (Reader.Read())
            {
                EmpCollection.Add(new EmployeeInfo()
                {
                    EmpNo = Convert.ToInt32(Reader["EmpNo"]),
                    EmpName = Reader["EmpName"].ToString(),
                    Salary = Convert.ToInt32(Reader["Salary"]),
                    DeptName = Reader["DeptName"].ToString(),
                    Designation = Reader["Designation"].ToString()
                });
            }
            Reader.Close();
            Conn.Close();
            return EmpCollection;
        }

        public EmployeeInfo NewEmployess()
        {
            return new EmployeeInfo();
        }
        private int GetLastEmpNo()
        {
            int EmpNo = 0;
            Conn.Open();
            Cmd = new SqlCommand();
            Cmd.Connection = Conn;
            Cmd.CommandText = "Select max(EmpNo) from EmployeeInfo";
            EmpNo = Convert.ToInt32(Cmd.ExecuteScalar());
            Conn.Close();
            return EmpNo;
        }
        public int InsertEmployee(EmployeeInfo objEmpInfo)
        {
            Conn.Open();
            Cmd = new SqlCommand();
            Cmd.Connection = Conn;
            Cmd.CommandText =
             "Insert into EmployeeInfo (EmpName,Salary,DeptName,Designation)" +
            "values(@EmpName,@Salary,@DeptName,@Designation)";
            Cmd.Parameters.AddWithValue("@EmpName", objEmpInfo.EmpName);
            Cmd.Parameters.AddWithValue("@Salary", objEmpInfo.Salary);
            Cmd.Parameters.AddWithValue("@DeptName", objEmpInfo.DeptName);
            Cmd.Parameters.AddWithValue("@Designation", objEmpInfo.Designation);
            Cmd.ExecuteNonQuery();
            Conn.Close();
            int EmpNo = GetLastEmpNo();
            return EmpNo;
        }
    }
}


The EmployeeInfoDAL class defines methods for Insert operation in the database table.

Step 2: Now we need to define Commanding layer for the application. This class defines an Action<T> delegate which corresponds to the command subscribed on the UI elements using InputBindings. We also need to define the model class which will define public properties used for UI databinding. The model class also defines public read-only ICommand properties which are configured with the KeyBinding on UI for shortcut keys. The code for the classes is as below:

using System;
using System.Windows.Input;
using System.ComponentModel;
using System.Collections.ObjectModel;
using System.Windows;

namespace WPF4_InptuBinding
{
/// <summary>
/// The class used to define source for the InputCommand Bindings 
/// on UI Elements like Button
/// </summary>
public class ApplicationModel : INotifyPropertyChanged
{
EmployeeInfoDAL objDal;
public ApplicationModel()
{
objDal = new EmployeeInfoDAL();
}

EmployeeInfo _ObjEmpInfo = new EmployeeInfo();
ObservableCollection<EmployeeInfo> _Employees;

public ObservableCollection<EmployeeInfo> Employees
{
get { return _Employees = objDal.GetEmployees(); }

}

public EmployeeInfo ObjEmpInfo
{
get { return _ObjEmpInfo; }
set
{
_ObjEmpInfo = value;
OnPropertyChanged("ObjEmpInfo");
}
}

int _EmpNo;

public int EmpNo
{
get { return _EmpNo; }
set
{
_EmpNo = value;
OnPropertyChanged("EmpNo");
}
}

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

private ICommand _SaveRecordBinding;

public ICommand SaveRecordBinding
{
get
{
this._SaveRecordBinding = this._SaveRecordBinding ?? 
new ActionCommand(res => EmpNo = 
objDal.InsertEmployee(ObjEmpInfo));
return this._SaveRecordBinding;
}
}

private ICommand _NewRecordBinding;

public ICommand NewRecordBinding
{
get
{
this._NewRecordBinding = this._NewRecordBinding ??
new ActionCommand(res => ObjEmpInfo = 
objDal.NewEmployess());

return this._NewRecordBinding;
}
}

}

/// <summary>
/// The class used to define the Command which is 
///further bound with the UI Elements
/// </summary>
public class ActionCommand : ICommand
{
Action<object> ExecuteCommand;

public ActionCommand(Action<object> executeCommand)
{
ExecuteCommand = executeCommand;
}

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

public event EventHandler CanExecuteChanged;

public void Execute(object parameter)
{
try
{
if (parameter == null)
{
MessageBox.Show("Please enter values");
}
else
{
ExecuteCommand(parameter);
}
}
catch (Exception ex)
{
MessageBox.Show("Error occured!!!Please make all entry");
}
}
}
}

Step 3: Open MainWindow.xaml and write the following Xaml:

<Window x:Class="WPF4_InptuBinding.MainWindow"

        xmlns=http://schemas.microsoft.com/winfx/2006/xaml/presentation”


        xmlns:x=http://schemas.microsoft.com/winfx/2006/xaml”


        xmlns:src="clr-namespace:WPF4_InptuBinding"


        Title="MainWindow" Height="530" Width="876">


    <Window.Resources>


        <src:ApplicationModel x:Key="AppModel"></src:ApplicationModel>


    </Window.Resources>


  
    <Window.InputBindings>


        <KeyBinding Command="{Binding ElementName=MainGrid,


    Path=DataContext.SaveRecordBinding}"


                    CommandParameter="{Binding ElementName=MainGrid,


            Path=DataContext.ObjEmpInfo,Mode=TwoWay}"


                                 Key="S"


                                 Modifiers="Control"></KeyBinding>


        <KeyBinding Command="{Binding ElementName=MainGrid,


                Path=DataContext.NewRecordBinding}"


                     CommandParameter="{Binding ElementName=MainGrid,


             Path=DataContext.ObjEmpInfo,Mode=TwoWay}"


                                 Key="N"


                                 Modifiers="Control"></KeyBinding>


    </Window.InputBindings>


   
    <Grid DataContext="{Binding Source={StaticResource AppModel}}" Name="MainGrid">


        <Grid.ColumnDefinitions>


            <ColumnDefinition Width="432*" />


            <ColumnDefinition Width="422*" />


        </Grid.ColumnDefinitions>


        <Grid Height="467" HorizontalAlignment="Left" Margin="12,12,0,0" Name="grid1"


              VerticalAlignment="Top" Width="412"


        DataContext="{Binding Path=ObjEmpInfo,Mode=TwoWay}">


            <Grid.RowDefinitions>


                <RowDefinition Height="42*" />


                <RowDefinition Height="40*" />


                <RowDefinition Height="40*" />


                <RowDefinition Height="44*" />


                <RowDefinition Height="44*" />


                <RowDefinition Height="257*" />


            </Grid.RowDefinitions>


            <Grid.ColumnDefinitions>


                <ColumnDefinition Width="202*" />


                <ColumnDefinition Width="210*" />


            </Grid.ColumnDefinitions>


            <TextBlock Name="textBlock1" Text="EmpNo:" />


            <TextBlock Grid.Row="1" Name="textBlock2" Text="EmpName:" />


            <TextBlock Grid.Row="2" Name="textBlock3" Text="Salary:" />


            <TextBlock Grid.Row="3" Name="textBlock4" Text="DeptName:" />


            <TextBlock Grid.Row="4" Name="textBlock5" Text="Deisgnation:" />


            <TextBox Grid.Column="1" Name="txteno"   
        Text="{Binding ElementName=MainGrid,Path=DataContext.EmpNo,Mode=TwoWay}" IsEnabled="False" />


            <TextBox Grid.Column="1" Grid.Row="1" Name="txtename" Text="{Binding EmpName,Mode=TwoWay}"/>


            <TextBox Grid.Column="1" Grid.Row="2" Name="txtsal" Text="{Binding Salary,Mode=TwoWay}"/>


            <TextBox Grid.Column="1" Grid.Row="3" Name="txtdname" Text="{Binding DeptName,Mode=TwoWay}"/>


            <TextBox Grid.Column="1" Grid.Row="4" Name="txtdesignation" Text="{Binding Designation,Mode=TwoWay}">


             
            </TextBox>


            <Button Content="New" Grid.Row="5" Height="31" HorizontalAlignment="Left"


                    Margin="6,21,0,0" Name="btnNew" VerticalAlignment="Top" Width="153"


                     Command="{Binding ElementName=MainGrid,Path=DataContext.NewRecordBinding}"


                     CommandParameter="{Binding ElementName=MainGrid,Path=DataContext.ObjEmpInfo,Mode=TwoWay}"/>


            <Button Content="Save" Height="31" HorizontalAlignment="Left"


                    Margin="26,21,0,0" Name="btnSave" VerticalAlignment="Top"


                    Width="153" Grid.Column="1" Grid.Row="5" Command="{Binding ElementName=MainGrid,


            Path=DataContext.SaveRecordBinding}"


                    CommandParameter="{Binding ElementName=MainGrid,Path=DataContext.ObjEmpInfo,Mode=TwoWay}">


            </Button>


            <TextBlock Grid.Row="5" Height="32" HorizontalAlignment="Left"


                       Margin="11,66,0,0" Name="textBlock6" Text="Press 'Ctrl+S' to Save the Record"


                       VerticalAlignment="Top" Grid.Column="1" Width="180"


                       FontWeight="SemiBold" />


            <TextBlock Height="55" HorizontalAlignment="Left" Margin="6,66,0,0"


                       Name="textBlock7" Text="Press 'Ctrl+N' for New Record Entry"


                       VerticalAlignment="Top" Width="180" Grid.Row="5"


                       TextWrapping="Wrap" FontWeight="SemiBold" />


        </Grid>


    </Grid>


</Window>



The above Xaml code has the following:

  • The model class is instantiated in the Windows Resources.
  • In the InputBindings of the Window, KeyBindings are defined for the related command properties defined in the Model class. This also defines shortcut key for executing the specific command on the model class using ‘S’ for Save and ‘N’ new operations, to which model class is making call using the Command properties.
  • Buttons present in the XAML are also set with the Command properties defined in the model class. This facility is provided for both type of users who are comfortable with Buttons and use Short-cut keys.
Step 4: Run the application and test it using the Shortcut keys.

After running the application enter values, tab out of the Designation TextBox and press ‘Ctrl+S’. The following result will be displayed and the EmpNo Textbox will show the EmpNo generated after insert operation completed. (EmpNo is auto-generated in Database table)


clip_image002


After pressing ‘Ctrl+N’, the following result will be displayed:


clip_image004

The first Textbox shows the last record added.




About The Author

Mahesh Sabnis is a Microsoft MVP having over 18 years 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). He also blogs regularly at DotNetCurry.com. Follow him on twitter @maheshdotnet

2 comments:

Unknown said...

Found best multi event command binder in codeplex

Multi Event Command Binder

Benefits:

1:Ability to bind multiple event to multiple commands.
2:In CommandArgs in command handler you can even get original event args this was not possible with existing command binders.
3:In CommandArgs in command handler you can even get original source of the event this was not possible with existing command binders.
4:In this you can control individual CanExecute methods.

Blogs
Multi Event Command BlogSpot
Multi Event Command WordPress

Anonymous said...

It is nice that u showed how commanding works, but why publish not related to the subject code, like EmployeeInfoDAL?