This Tutorial will explain how to implement crud operations in WPF with MVVM Framework.
Open Visual Studio 2012. Go to create new project tab. Select WPF Application.
Add Five folders to the Application.
- Model
- View
- ViewModel
- Data
- Helpers
Design the Data Grid in WPF
Create a MainWindow.xaml file inside View folder in your appliction. Add the below code to the file.
<Grid Margin="0,0,0,-1"> <Grid.RowDefinitions> <RowDefinition Height="0.939*"/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <GroupBox Header="Employee Data" HorizontalAlignment="Center" VerticalAlignment="Center" Height="383" Margin="5,5,5,5"> <Grid> <Grid.RowDefinitions> <RowDefinition /> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <ScrollViewer VerticalScrollBarVisibility="Auto" Margin="5,5,5,5"> <StackPanel> <StackPanel Orientation="Horizontal" Margin="5,5,5,5"> <DataGrid x:Name="dg1" ItemsSource="{Binding Employee}" SelectedItem="{Binding SelectedEmployee}" CanUserAddRows="False" CanUserDeleteRows="False" SelectionMode="Single" SelectedIndex="{Binding SelectedIndex}" VerticalAlignment="Top" AutoGenerateColumns="False" Margin="5,5,5,5"> <DataGrid.Columns> <DataGridTextColumn Header="Emp ID" Binding="{Binding Path=ID}"></DataGridTextColumn> <DataGridTextColumn Header="First Name" Binding="{Binding Path=FirstName}"></DataGridTextColumn> <DataGridTextColumn Header="Last Name" Binding="{Binding Path=LastName}"></DataGridTextColumn> <DataGridTextColumn Header="DOB" Binding="{Binding Path=DOB,StringFormat=d}"></DataGridTextColumn> <DataGridTextColumn Header="Gender" Binding="{Binding Path=Gender}"></DataGridTextColumn> <DataGridTextColumn Header="Nationality" Binding="{Binding Path=Nationality}"></DataGridTextColumn> <DataGridTextColumn Header="Language" Binding="{Binding Path=Language}"></DataGridTextColumn> <DataGridTextColumn Header="Address" Binding="{Binding Path=Address}"></DataGridTextColumn> </DataGrid.Columns> </DataGrid> </StackPanel> </StackPanel> </ScrollViewer> <Button Grid.Row="1" Content="Add Employee" Command="{Binding AddUserCommand}" Margin="5" Focusable="False" HorizontalAlignment="Left" MinWidth="200"/> </Grid> </GroupBox> </Grid>
Create a User.cs class inside Model folder in your Application. Write the below code in the file.
class User : INotifyPropertyChanged { private string _id; private string _firstName; private string _address; private string _lastName; private string _language; private string _dob; private string _nationality; private string _gender; private bool _male; private bool _female; private bool _hindi; private bool _english; private bool _french; public User() { } public string ID { get { return _id; } set { _id = value; NotifyOfPropertyChange("ID"); } } public string FirstName { get { return _firstName; } set { _firstName = value; NotifyOfPropertyChange("FirstName"); } } public string LastName { get { return _lastName; } set { _lastName = value; NotifyOfPropertyChange("LastName"); } } public string Address { get { return _address; } set { _address = value; NotifyOfPropertyChange("Address"); } } public string Language { get { return _language; } set { _language = string.Empty; string Comma = string.Empty; if (Hindi) { _language = "Hindi"; } if (English) { if (!string.IsNullOrEmpty(_language)) { Comma = ","; } _language = _language + Comma + "English"; } if (French) { if (!string.IsNullOrEmpty(_language)) { Comma = ","; } _language = _language + Comma + "French"; } NotifyOfPropertyChange("Language"); } } public string DOB { get { return _dob; } set { _dob = Convert.ToDateTime(value).ToString("MM/dd/yyyy"); NotifyOfPropertyChange("DOB"); } } public string Nationality { get { return _nationality; } set { _nationality = value; NotifyOfPropertyChange("Nationality"); } } public bool Male { get { return _male; } set { _male = value; Gender = "Male"; NotifyOfPropertyChange("Male"); } } public bool Female { get { return _female; } set { _female = value; Gender = "Female"; NotifyOfPropertyChange("Female"); } } public string Gender { get { return _gender; } set { if (Male) { _gender = "Male"; } if (Female) { _gender = "Female"; } NotifyOfPropertyChange("Gender"); } } public bool Hindi { get { return _hindi; } set { _hindi = value; Language = "Hindi"; NotifyOfPropertyChange("Hindi"); } } public bool English { get { return _english; } set { _english = value; Language = "English"; NotifyOfPropertyChange("English"); } } public bool French { get { return _french; } set { _french = value; Language = "French"; NotifyOfPropertyChange("French"); } } #region INotifyPropertyChanged Members public event PropertyChangedEventHandler PropertyChanged; private void NotifyOfPropertyChange(string propertyName) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } #endregion }
Bind this data grid to ViewModelUser. Write the below code to MainWindow.xaml.cs file.
this.DataContext = new ViewModelUser();
Create a ViewModelUser.cs class inside ViewModel folder in your application. Add the below code to this class file.
PersonnelBusinessObject personnel; ObservableCollection<User> _Employee; public ViewModelUser() { personnel = new PersonnelBusinessObject(); } public ObservableCollection<User> Employee { get { _Employee = new ObservableCollection<User>(personnel.GetEmployees()); return _Employee; } }
PersonnelBusinessObject Class is used to write business logic. Create a PersonnelBusinessObject.cs in Model folder of your application.
class PersonnelBusinessObject { List<User> Employee { get; set; } public PersonnelBusinessObject() { Employee = DatabaseLayer.GetEmployeeFromDatabase(); } public List<User> GetEmployees() { return Employee = DatabaseLayer.GetEmployeeFromDatabase(); } }
DatabaseLayer class is used to communicate with database. you can write your stored procedure and queries to fetch data from database.
Create a DatabaseLayer.cs class in Data Folder in your application. Write the below code in this class.
class DatabaseLayer { public static List<User> GetEmployeeFromDatabase() { try { DataTable dt = SqlHelper.ExecuteDataTable(AppConstants.getConnectionString(), CommandType.StoredProcedure, "[dbo].[uspGetUser]"); var Employee = new List<User>(); foreach (DataRow row in dt.Rows) { var obj = new User() { ID = (string)row["ID"], FirstName = (string)row["FirstName"], LastName = (string)row["LastName"], DOB = (string)row["DOB"], Gender = (string)row["Gender"], Nationality = (string)row["Nationality"], Language = ((string)row["Language"]), Address = (string)row["Address"], Male = (bool)row["Male"], Female = (bool)row["Female"], Hindi = (bool)row["Hindi"], English = (bool)row["English"], French = (bool)row["French"], }; Employee.Add(obj); } return Employee; } catch (Exception ex) { throw ex; } } }
After building and running the application data will be populate in the Data grid.
Implementing Insert, Update and Delete operations in WPF with MVVM Framework.
Open the MainWindow.xaml file and add the below code just above to the existing code.
<Window.Resources> <!-- This converts SelectedItem to a collection, for use in the ItemsControl --> <helpers:SelectedItemToItemsSource x:Key="SelectedItemToItemsSource"/> <!-- This is the template for the user form, used by the itemsControl below --> <DataTemplate x:Key="UserGrid"> <Border Background="Chocolate" BorderBrush="Black" BorderThickness="1" CornerRadius="5" > <Grid Margin="10"> <Grid.RowDefinitions> <RowDefinition/> <RowDefinition/> <RowDefinition/> <RowDefinition/> <RowDefinition/> <RowDefinition/> <RowDefinition/> <RowDefinition/> <RowDefinition/> <RowDefinition/> <RowDefinition/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition/> <ColumnDefinition/> </Grid.ColumnDefinitions> <TextBlock Text="Emp ID" Grid.Row="1" Grid.Column="0"/> <TextBlock Text="First Name" Grid.Row="2" Grid.Column="0"/> <TextBlock Text="Last Name" Grid.Row="3" Grid.Column="0"/> <TextBlock Text="Address" Grid.Row="4" Grid.Column="0"/> <TextBlock Text="Gender" Grid.Row="5" Grid.Column="0"/> <TextBlock Text="Language" Grid.Row="6" Grid.Column="0"/> <TextBlock Text="Nationality" Grid.Row="7" Grid.Column="0"/> <TextBlock Text="DOB" Grid.Row="8" Grid.Column="0"/> <TextBox Text="{Binding ID, BindingGroupName=Group1, UpdateSourceTrigger=Explicit}" Grid.Column="1" Grid.Row="1"/> <TextBox Text="{Binding FirstName, BindingGroupName=Group1, UpdateSourceTrigger=Explicit}" Grid.Column="1" Grid.Row="2"/> <TextBox Text="{Binding LastName, BindingGroupName=Group1, UpdateSourceTrigger=Explicit}" Grid.Column="1" Grid.Row="3"/> <TextBox Text="{Binding Address, BindingGroupName=Group1, UpdateSourceTrigger=Explicit}" Grid.Column="1" Grid.Row="4"/> <StackPanel Orientation="Horizontal" Grid.Column="1" Grid.Row="5"> <RadioButton Name="radMale" GroupName="Gender" Content="Male" IsChecked="{Binding Male, BindingGroupName=Group1, UpdateSourceTrigger=Explicit}"></RadioButton> <RadioButton Name="radFemale" GroupName="Gender" Content="Female" IsChecked="{Binding Female, BindingGroupName=Group1, UpdateSourceTrigger=Explicit}"></RadioButton> </StackPanel> <StackPanel Orientation="Horizontal" Grid.Column="1" Grid.Row="6"> <CheckBox IsChecked="{Binding Hindi, BindingGroupName=Group1, UpdateSourceTrigger=Explicit}">Hindi</CheckBox> <CheckBox IsChecked="{Binding English, BindingGroupName=Group1, UpdateSourceTrigger=Explicit}">English</CheckBox> <CheckBox IsChecked="{Binding French, BindingGroupName=Group1, UpdateSourceTrigger=Explicit}">French</CheckBox> </StackPanel> <ComboBox DisplayMemberPath="Nationality" SelectedValuePath="Nationality" SelectedValue="{Binding Nationality}" ItemsSource="{Binding DataContext.NationalityList, UpdateSourceTrigger=Explicit, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ItemsControl}}}" Margin="5,0,0,5" VerticalAlignment="Top" Grid.Column="1" Grid.Row="7"/> <DatePicker x:Name="dpDOB" Grid.Column="1" Grid.Row="8" HorizontalAlignment="Left" SelectedDateFormat="Short" SelectedDate="{Binding DOB, Mode=TwoWay}"/> <StackPanel Orientation="Horizontal" Grid.Row="10" Grid.ColumnSpan="2" HorizontalAlignment="Right" Margin="5,5,5,5"> <Button Foreground="White" Background="Green" Content="Cancel" Command="{Binding DataContext.CancelCommand, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}" Margin="4,0"/> <Button Foreground="White" Background="Green" Content="Delete" Command="{Binding DataContext.DeleteUserCommand, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}" Margin="4,0"/> <Button Foreground="White" Background="Green" Content="Save" Command="{Binding DataContext.SaveCommand, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}" Margin="4,0"/> </StackPanel> </Grid> </Border> </DataTemplate> </Window.Resources>
Add the below line of code just after the Data Grid.
<ItemsControl BindingGroup="{Binding UpdateBindingGroup, Mode=OneWay}" ItemTemplate="{StaticResource UserGrid}" ItemsSource="{Binding SelectedEmployee, Converter={StaticResource SelectedItemToItemsSource}}" VerticalAlignment="Top" Margin="5,5,5,5"/>
Helper class is used to converts SelectedItem to a collection, for use in the ItemsControl.
Add the Helper class reference to the MainWindow.xaml.
xmlns:helpers="clr-namespace:WpfCrudeOperations.Helpers"
Create a Converters.cs class in the Helpers folder and add the below line of code in the class file
public class SelectedItemToItemsSource : IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { if (value == null) return null; return new List<object>() { value }; } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotImplementedException(); } }
ViewModelUser uses a ViewModelBase.cs class to implement INotifyPropertyChanged event. Create a ViewModelBase.cs class inside ViewModel folder in your application.
class ViewModelBase : INotifyPropertyChanged { //basic ViewModelBase internal void RaisePropertyChanged(string prop) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(prop)); } } public event PropertyChangedEventHandler PropertyChanged; //Extra Stuff, shows why a base ViewModel is useful bool? _CloseWindowFlag; public bool? CloseWindowFlag { get { return _CloseWindowFlag; } set { _CloseWindowFlag = value; RaisePropertyChanged("CloseWindowFlag"); } } public virtual void CloseWindow(bool? result = true) { Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() => { CloseWindowFlag = CloseWindowFlag == null ? true : !CloseWindowFlag; })); } }
Buttons “Add Employee”, “Save”, “Cancel” and “Delete” uses Relay command to communicate with ViewModel.
Add the below line of code in ViewModelUser.cs class.
public RelayCommand CancelCommand { get; set; } public RelayCommand SaveCommand { get; set; } public RelayCommand AddUserCommand { get; set; } public RelayCommand DeleteUserCommand { get; set; }
To call these commands we have to declared methods for these commands on ViewModelUser constructor.
CancelCommand = new RelayCommand(DoCancel); SaveCommand = new RelayCommand(DoSave); AddUserCommand = new RelayCommand(AddUser); DeleteUserCommand = new RelayCommand(DeleteUser);
Define methods
void DoCancel(object param) { UpdateBindingGroup.CancelEdit(); if (SelectedIndex == -1) //This only closes if new - just to show you how CancelEdit returns old values to bindings SelectedEmployee = null; } void DoSave(object param) { UpdateBindingGroup.CommitEdit(); var employee = SelectedEmployee as User; if (SelectedIndex == -1) { personnel.AddEmployee(employee); RaisePropertyChanged("Employee"); // Update the list from the data source } else personnel.UpdateEmployee(employee); SelectedEmployee = null; } void AddUser(object parameter) { SelectedEmployee = null; // Unselects last selection. Essential, as assignment below won't clear other control's SelectedItems var employee = new User(); SelectedEmployee = employee; } void DeleteUser(object parameter) { var employee = SelectedEmployee as User; if (SelectedIndex != -1) { personnel.DeleteEmployee(employee); RaisePropertyChanged("Employee"); // Update the list from the data source } else SelectedEmployee = null; // Simply discard the new object }
Add RelayCommand.cs class file in ViewModel Folder in your application.
class RelayCommand : ICommand { #region Fields readonly Action<object> _execute; readonly Predicate<object> _canExecute; #endregion // Fields #region Constructors public RelayCommand(Action<object> execute) : this(execute, null) { } public RelayCommand(Action<object> execute, Predicate<object> canExecute) { if (execute == null) throw new ArgumentNullException("execute"); _execute = execute; _canExecute = canExecute; } #endregion // Constructors #region ICommand Members public bool CanExecute(object parameter) { return _canExecute == null ? true : _canExecute(parameter); } public event EventHandler CanExecuteChanged { add { CommandManager.RequerySuggested += value; } remove { CommandManager.RequerySuggested -= value; } } public void Execute(object parameter) { _execute(parameter); } #endregion // ICommand Members }
To Populate Nationality ComboBox. Write the below code in ViewModelUser.cs class.
public ObservableCollection<NationalityCollection> NationalityList { get { _nationalityCollection = new ObservableCollection<NationalityCollection>(personnel.GetNationality()); return _nationalityCollection; } }
Add a new class NationalityCollection.cs in Model folder to get Nationality collection.
class NationalityCollection : INotifyPropertyChanged { ICollectionView _nationalityCollection; public ICollectionView NationalityView { get { return _nationalityCollection; } set { if (value != _nationalityCollection) { _nationalityCollection = value; NotifyOfPropertyChange("NationalityCollection"); } } } public string Nationality { get { return _nationality; } set { _nationality = value; NotifyOfPropertyChange("Nationality"); } } #region INotifyPropertyChanged Members public event PropertyChangedEventHandler PropertyChanged; private string _nationality; private void NotifyOfPropertyChange(string propertyName) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } #endregion }
Add below methods in the PersonnelBusinessObject.cs class
List<NationalityCollection> NationalityCollection { get; set; } public List<NationalityCollection> GetNationality() { return NationalityCollection = DatabaseLayer.GetNationality(); } public void AddEmployee(User employee) { DatabaseLayer.InsertEmployee(employee); OnEmployeeChanged(); } public void UpdateEmployee(User employee) { DatabaseLayer.UpdateEmployee(employee); OnEmployeeChanged(); } public void DeleteEmployee(User employee) { DatabaseLayer.DeleteEmployee(employee); OnEmployeeChanged(); } void OnEmployeeChanged() { if (EmployeeChanged != null) EmployeeChanged(this, null); }
Add below methods in DatabaseLayer.cs class
internal static void InsertEmployee(User employee) { try { SqlParameter[] MyParams = new SqlParameter[8]; MyParams[0] = new SqlParameter("@ID", employee.ID); MyParams[1] = new SqlParameter("@FirstName", employee.FirstName); MyParams[2] = new SqlParameter("@LastName", employee.LastName); MyParams[3] = new SqlParameter("@Gender", employee.Gender); MyParams[4] = new SqlParameter("@DOB", employee.DOB); MyParams[5] = new SqlParameter("@Language", employee.Language); MyParams[6] = new SqlParameter("@Nationality", employee.Nationality); MyParams[7] = new SqlParameter("@Address", employee.Address); SqlHelper.ExecuteNonQuery(AppConstants.getConnectionString(), CommandType.StoredProcedure, "[dbo].[uspInsertUser]", MyParams); MessageBox.Show("Data Saved Successfully."); } catch (SqlException ex) { throw ex; } finally { } } internal static void UpdateEmployee(User employee) { try { SqlParameter[] MyParams = new SqlParameter[8]; MyParams[0] = new SqlParameter("@ID", employee.ID); MyParams[1] = new SqlParameter("@FirstName", employee.FirstName); MyParams[2] = new SqlParameter("@LastName", employee.LastName); MyParams[3] = new SqlParameter("@Gender", employee.Gender); MyParams[4] = new SqlParameter("@DOB", employee.DOB); MyParams[5] = new SqlParameter("@Language", employee.Language); MyParams[6] = new SqlParameter("@Nationality", employee.Nationality); MyParams[7] = new SqlParameter("@Address", employee.Address); SqlHelper.ExecuteNonQuery(AppConstants.getConnectionString(), CommandType.StoredProcedure, "[dbo].[uspInsertUser]", MyParams); MessageBox.Show("Data Updated Successfully."); } catch (SqlException ex) { throw ex; } finally { } } internal static void DeleteEmployee(User employee) { try { SqlParameter[] MyParams = new SqlParameter[1]; MyParams[0] = new SqlParameter("@ID", employee.ID); SqlHelper.ExecuteNonQuery(AppConstants.getConnectionString(), CommandType.StoredProcedure, "[dbo].[uspDeletetUser]", MyParams); MessageBox.Show("Data Deleted Successfully."); } catch (SqlException ex) { throw ex; } finally { } } public static List<NationalityCollection> GetNationality() { try { DataTable dt = SqlHelper.ExecuteDataTable(AppConstants.getConnectionString(), CommandType.StoredProcedure, "[dbo].[uspGetNationality]"); var NationalityList = new List<NationalityCollection>(); foreach (DataRow row in dt.Rows) { var obj = new NationalityCollection() { Nationality = (string)row["Nationality"] }; NationalityList.Add(obj); } return NationalityList; // return dt; } catch (Exception ex) { throw ex; } }
To Implement employee change event. add these line of code in ViewModelUser class constructor.
personnel.EmployeeChanged += new EventHandler(personnel_EmployeeChanged); UpdateBindingGroup = new BindingGroup { Name = "Group1" };
Method “personnel_EmployeeChanged” definition.
void personnel_EmployeeChanged(object sender, EventArgs e) { Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() => { RaisePropertyChanged("Employee"); })); }
BindingGroup _UpdateBindingGroup; public BindingGroup UpdateBindingGroup { get { return _UpdateBindingGroup; } set { if (_UpdateBindingGroup != value) { _UpdateBindingGroup = value; RaisePropertyChanged("UpdateBindingGroup"); } } }
To get the selected employee from the data grid. Add the below code in ViewModelUser class.
public int SelectedIndex { get; set; } object _SelectedEmployee; public object SelectedEmployee { get { return _SelectedEmployee; } set { if (_SelectedEmployee != value) { _SelectedEmployee = value; RaisePropertyChanged("SelectedEmployee"); } } }
SQL Helper Class
http://www.codesolution.org/sql-helper-class-in-asp-net/
After building and running the application result will be as.
In very Simple Way You discuss the WPF, It helps to build my first WPF application.
Very Helpfull…………
Thanks For Sharing
Hi Sir,
very nice article, Please could you send me the source code of all, I am trying the above code in my system but. I am not able to do it.
Please send me at sunilkmr284@gmail.com
could you please send me the source code of this example.
can you please mail this source code on my email address?
zlk.shah@gmail.com
very nice article ,i have tried it but not able to implement it on my system,can you please send whole source code on above mentioned email address
how impliment EmployeeChanged method?please send me source code .
My email is rajon98@yahoo.com
plz send me source code @ sonali.sawardekar@ymail.com
please send me source code.It is very helpful article.
The article is very useful. I am implementing the code but at some places I m facing problems. I would be grateful to you if you could send me the source code.
Pls send the source code on my email jyotiyadav.ind@gmail.com.
Useful Article, can you please send SqlHelper class on nitin.p@msp-group.co.uk
SQL Helper Class
http://www.codesolution.org/sql-helper-class-in-asp-net/
super Article, can you please send SqlHelper class on nitin.p@msp-group.co.uk
Useful Article, can you please send source code on mouhssine1.ntic@gmail.com
can you send sqlhelper class on this address : ansari.sheebu786@gmail.com
Nice article ! could you send me source code @ vickybhardwaj200@gmail.com
Its very good article. You can send the code to my email renaisanci@gmail.com
i would like to see source code for comparison as i am having issues
there is no mention anywhere about code of stored procedures
script for creating SP must be provided
Nice article ! could you send me SCRIPT FOR stored procedure & table creation at thanks nirankar.kaushik@gmail.com
Nice article THANKS ! could you send me source code @ dnyaneshpatil555@gmail.com
could you send me source code @ sonali.sawardekar@ymail.com
It’s very clear and usefull , thanks!!!!
Could you please send me the code to compare what i ve done and to correct some issues ,at touabdelhak@yahoo.fr
cheers;
very userfull for me!
could you please send source code , thank you
yes123430@gmail.com
I want to show source code, can you please mail me the source code?
my email id is zlk.shah@gmail.com
Thanks.
please send me the full source code,thank you
Nice article THANKS ! could you send me full source code @ kiranpalem@gmail.com
Nice Article please send me the full source code
Dear I am getting Error on AppConstants in DataBaseLayer.cs file
AppConstants file contains only connection to database. You can simply add your connection string.
It’s very clear and usefull , thanks!!!!
Could you please send me the code to compare what i ve done and to correct some issues ,at
rahulsharma7884@yahoo.com
Hi ,
Thanks for the great tutorial! Could it be possible to send me also the source code to compare with mine?
Regards,
legion_dracul@hotmail.com
Thanks,very nice article for start wpf .
sir please send me source code or sql stored procedure and table code on
gmail: valekarraju@gmail.com
Nice article, could u send me the source code @tupkargovinda@gmail.com