using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.Composition;
using System.Linq;
using System.Windows;
using System.Windows.Data;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using GalaSoft.MvvmLight.Messaging;
using SchoolSample.Common;
using SchoolSample.EntityModel;
namespace SchoolSample.ViewModel
{
[Export(ViewModelTypes.InstructorPageViewModel, typeof(ViewModelBase))]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class InstructorPageViewModel : ViewModelBase
{
#region "Private Data Members"
private ISchoolModel _schoolModel;
#endregion "Private Data Members"
#region "Constructor"
[ImportingConstructor]
public InstructorPageViewModel(ISchoolModel schoolModel)
{
_schoolModel = schoolModel;
// set up event handling
_schoolModel.PropertyChanged += _schoolModel_PropertyChanged;
_schoolModel.GetInstructorsCompleted += _schoolModel_GetInstructorsCompleted;
_schoolModel.GetInstructorByIdCompleted += _schoolModel_GetInstructorByIdCompleted;
_schoolModel.SaveInstructorChangesCompleted += _schoolModel_SaveInstructorChangesCompleted;
PropertyChanged += InstructorPageViewModel_PropertyChanged;
// get instructors
_schoolModel.GetInstructorsAsync("Courses", "InstructorPage");
// set up initial screen status
InstructorFormEndEdit();
}
#endregion "Constructor"
#region "Public Properties"
/// <summary>
/// All instructors collection returns/updates InstructorsList from _schoolModel,
/// _allInstructorsCache keeps a local reference to the list and it syncs with
/// _schoolModel.InstructorsList when navigates to this page
/// </summary>
private ObservableCollection<Instructor> AllInstructors
{
get
{
return _schoolModel != null ? _schoolModel.InstructorsList : null;
}
set
{
if (!ReferenceEquals(_allInstructorsCache, value))
{
_allInstructorsCache = value;
// update InstructorsList on the model
if (_schoolModel != null) _schoolModel.InstructorsList = _allInstructorsCache;
}
}
}
private ObservableCollection<Instructor> _allInstructorsCache;
public CollectionViewSource AllInstructorsSource
{
get { return _allInstructorsSource; }
private set
{
if (!ReferenceEquals(_allInstructorsSource, value))
{
_allInstructorsSource = value;
RaisePropertyChanged("AllInstructorsSource");
}
}
}
private CollectionViewSource _allInstructorsSource;
/// <summary>
/// CurrentInstructor returns/updates CurrentInstructor from _schoolModel,
/// _currentInstructorCache keeps a local reference to the object and it
/// syncs with _schoolModel.CurrentInstructor when navigates to this page
/// </summary>
public Instructor CurrentInstructor
{
get
{
return _schoolModel != null ? _schoolModel.CurrentInstructor : null;
}
private set
{
if (!ReferenceEquals(_currentInstructorCache, value))
{
_currentInstructorCache = value;
if (_schoolModel != null) _schoolModel.CurrentInstructor = _currentInstructorCache;
RaisePropertyChanged("CurrentInstructor");
}
}
}
private Instructor _currentInstructorCache;
public bool InstructorFormInEdit
{
get { return _instructorFormInEdit; }
private set
{
if (_instructorFormInEdit != value)
{
_instructorFormInEdit = value;
RaisePropertyChanged("InstructorFormInEdit");
}
}
}
private bool _instructorFormInEdit;
public bool CurrentInstructorHasErrors
{
get { return _currentInstructorHasErrors; }
set
{
if (_currentInstructorHasErrors != value)
{
_currentInstructorHasErrors = value;
RaisePropertyChanged("CurrentInstructorHasErrors");
}
}
}
private bool _currentInstructorHasErrors;
public bool InstructorListIsEnabled
{
get { return _instructorListIsEnabled; }
private set
{
if (_instructorListIsEnabled != value)
{
_instructorListIsEnabled = value;
RaisePropertyChanged("InstructorListIsEnabled");
}
}
}
private bool _instructorListIsEnabled = true;
#endregion "Public Properties"
#region "Public Commands"
/// <summary>
/// Command for Page Loaded event
/// </summary>
public RelayCommand PageLoadedCommand
{
get
{
if (_pageLoadedCommand == null)
{
_pageLoadedCommand = new RelayCommand(
OnPageLoadedCommand);
}
return _pageLoadedCommand;
}
}
private RelayCommand _pageLoadedCommand;
private void OnPageLoadedCommand()
{
// synchronize _schoolModel.InstructorsList with local cache
if (_schoolModel != null && _allInstructorsCache != null)
_schoolModel.InstructorsList = _allInstructorsCache;
// synchronize _schoolModel.CurrentInstructor with local cache
if (_schoolModel != null && _currentInstructorCache != null)
_schoolModel.CurrentInstructor = _currentInstructorCache;
}
/// <summary>
/// Command for Page Unloaded event
/// </summary>
public RelayCommand PageUnLoadedCommand
{
get
{
if (_pageUnLoadedCommand == null)
{
_pageUnLoadedCommand = new RelayCommand(
OnPageUnLoadedCommand);
}
return _pageUnLoadedCommand;
}
}
private RelayCommand _pageUnLoadedCommand;
private void OnPageUnLoadedCommand()
{
// clean up
if (_schoolModel != null)
{
_schoolModel.InstructorsList = null;
_schoolModel.CurrentInstructor = null;
}
}
/// <summary>
/// Add instructor command. We can add a new
/// instructor when instructor form is not in edit
/// </summary>
public RelayCommand AddInstructorCommand
{
get
{
if (_addInstructorCommand == null)
{
_addInstructorCommand = new RelayCommand(
OnAddInstructorCommand,
() => !InstructorFormInEdit);
}
return _addInstructorCommand;
}
}
private RelayCommand _addInstructorCommand;
private void OnAddInstructorCommand()
{
// create a temporary companyId
int newPersonId = AllInstructors.Count > 0
? ((from instructor in AllInstructors select Math.Abs(instructor.PersonId)).Max() + 1) * (-1)
: -1;
// create a new instructor
CurrentInstructor = new Instructor
{
PersonId = newPersonId,
Name = string.Empty,
HireDate = DateTime.Now,
Salary = 0
};
// and begin edit
OnEditCommitInstructorCommand();
}
/// <summary>
/// Delete instructor command. We can delete a instructor
/// when the current instructor is not null, and instructor
/// form is not in edit
/// </summary>
public RelayCommand DeleteInstructorCommand
{
get
{
if (_deleteInstructorCommand == null)
{
_deleteInstructorCommand = new RelayCommand(
OnDeleteInstructorCommand,
() => (CurrentInstructor != null) && !InstructorFormInEdit);
}
return _deleteInstructorCommand;
}
}
private RelayCommand _deleteInstructorCommand;
private void OnDeleteInstructorCommand()
{
try
{
if (CurrentInstructor != null && CurrentInstructor.Courses.Count == 0)
{
// ask to confirm deleting the current instructor
DialogMessage dialogMessage = new DialogMessage(
this,
ApplicationStrings.DeleteCurrentInstructorMessageBoxText,
s =>
{
if (s == MessageBoxResult.OK)
{
if (CurrentInstructor.ChangeTracker.State == ObjectState.Added)
{
// if State is Added, simply remove from the InstructorsList
// as the database does not have this record yet
AllInstructors.Remove(CurrentInstructor);
}
else
{
// if confirmed, remove current instructor
CurrentInstructor.MarkAsDeleted();
_schoolModel.SaveInstructorChangesAsync(false);
}
}
})
{
Button = MessageBoxButton.OKCancel,
Caption = ApplicationStrings.ConfirmMessageBoxCaption
};
AppMessages.PleaseConfirmMessage.Send(dialogMessage);
}
else
{
DialogMessage dialogMessage = new DialogMessage(
this,
ErrorResources.CurrentInstructorCourseNotEmptyMessageBoxText,
null)
{
Button = MessageBoxButton.OK,
Caption = ApplicationStrings.WarningMessageBoxCaption
};
AppMessages.StatusUpdateMessage.Send(dialogMessage);
}
}
catch (Exception ex)
{
// notify user if there is any error
AppMessages.RaiseErrorMessage.Send(ex);
}
}
/// <summary>
/// Edit/Commit a instructor. We can edit/commit a instructor
/// when current instructor is not null
/// </summary>
public RelayCommand EditCommitInstructorCommand
{
get
{
if (_editCommitInstructorCommand == null)
{
_editCommitInstructorCommand = new RelayCommand(
OnEditCommitInstructorCommand,
() => (CurrentInstructor != null));
}
return _editCommitInstructorCommand;
}
}
private RelayCommand _editCommitInstructorCommand;
private void OnEditCommitInstructorCommand()
{
if (CurrentInstructor != null)
{
if (InstructorFormInEdit)
{
// if passed validation, end editing
if (!CurrentInstructorHasErrors && CurrentInstructor.TryValidate())
{
InstructorFormEndEdit();
// if this is a new instructor, add to the InstructorsList
if (CurrentInstructor.ChangeTracker.State == ObjectState.Added)
{
var alreadyAdded = AllInstructors.Any(n => n.PersonId == CurrentInstructor.PersonId);
if (!alreadyAdded)
{
AllInstructors.Add(CurrentInstructor);
AllInstructorsSource.View.MoveCurrentTo(CurrentInstructor);
}
}
}
}
else
{
InstructorFormBeginEdit();
}
}
}
/// <summary>
/// Cancel edit a instructor. We can cancel edit a instructor
/// when current instructor is not null, and the instructor form
/// is currently in edit
/// </summary>
public RelayCommand CancelEditInstructorCommand
{
get
{
if (_cancelEditInstructorCommand == null)
{
_cancelEditInstructorCommand = new RelayCommand(
OnCancelEditInstructorCommand,
() => (CurrentInstructor != null) && InstructorFormInEdit);
}
return _cancelEditInstructorCommand;
}
}
private RelayCommand _cancelEditInstructorCommand;
private void OnCancelEditInstructorCommand()
{
if (CurrentInstructor != null)
{
InstructorFormCancelEdit();
// if this is a new instructor, simply discard that record
if (CurrentInstructor.ChangeTracker.State == ObjectState.Added)
{
var alreadyAdded = AllInstructors.Any(n => n.PersonId == CurrentInstructor.PersonId);
if (!alreadyAdded)
{
CurrentInstructor = null;
AllInstructorsSource.View.MoveCurrentToFirst();
if (AllInstructorsSource.View.CurrentItem != null)
CurrentInstructor = (Instructor)AllInstructorsSource.View.CurrentItem;
}
}
}
}
/// <summary>
/// Refresh current instructor command. We can refresh
/// current instructor when instructor form is not in edit,
/// and there is no change to save for the current
/// instructor
/// </summary>
public RelayCommand RefreshInstructorCommand
{
get
{
if (_refreshInstructorCommand == null)
{
_refreshInstructorCommand = new RelayCommand(
OnRefreshInstructorCommand,
() => !InstructorFormInEdit && (_schoolModel != null)
&& !(_schoolModel.CurrentInstructorHasChanges));
}
return _refreshInstructorCommand;
}
}
private RelayCommand _refreshInstructorCommand;
private void OnRefreshInstructorCommand()
{
if (CurrentInstructor != null)
{
_schoolModel.GetInstructorByIdAsync(CurrentInstructor.PersonId);
}
}
/// <summary>
/// Submit change command. We can submit changes
/// when instructor form is not in edit, and there
/// are changes to save for current instructor
/// </summary>
public RelayCommand SubmitInstructorChangeCommand
{
get
{
if (_submitInstructorChangeCommand == null)
{
_submitInstructorChangeCommand = new RelayCommand(
OnSubmitInstructorChangeCommand,
() => !InstructorFormInEdit && (_schoolModel != null)
&& (_schoolModel.CurrentInstructorHasChanges));
}
return _submitInstructorChangeCommand;
}
}
private RelayCommand _submitInstructorChangeCommand;
private void OnSubmitInstructorChangeCommand()
{
try
{
if (!_schoolModel.IsBusy && CurrentInstructor != null)
{
// we only save changes when current instructor passed validation
var passedValidation = CurrentInstructor.TryValidateObjectGraph();
if (!passedValidation) return;
// save changes for CurrentInstructor only
_schoolModel.SaveInstructorChangesAsync(false);
}
}
catch (Exception ex)
{
// notify user if there is any error
AppMessages.RaiseErrorMessage.Send(ex);
}
}
/// <summary>
/// Cancel change command. We can cancel changes
/// when instructor form is not in edit, and there
/// are changes to cancel for current instructor
/// </summary>
public RelayCommand CancelInstructorChangeCommand
{
get
{
if (_cancelInstructorChangeCommand == null)
{
_cancelInstructorChangeCommand = new RelayCommand(
OnCancelInstructorChangeCommand,
() => !InstructorFormInEdit && (_schoolModel != null)
&& (_schoolModel.CurrentInstructorHasChanges));
}
return _cancelInstructorChangeCommand;
}
}
private RelayCommand _cancelInstructorChangeCommand;
private void OnCancelInstructorChangeCommand()
{
try
{
if (!_schoolModel.IsBusy && CurrentInstructor != null)
{
// ask to confirm canceling changes
DialogMessage dialogMessage = new DialogMessage(
this,
ApplicationStrings.CancelAnyChangesMessageBoxText,
s =>
{
if (s == MessageBoxResult.OK)
{
// if confirmed, cancel changes for CurrentInstructor
_schoolModel.RejectInstructorChanges(false);
}
})
{
Button = MessageBoxButton.OKCancel,
Caption = ApplicationStrings.ConfirmMessageBoxCaption
};
AppMessages.PleaseConfirmMessage.Send(dialogMessage);
}
}
catch (Exception ex)
{
// notify user if there is any error
AppMessages.RaiseErrorMessage.Send(ex);
}
}
/// <summary>
/// Refresh instructors list command. We can
/// refresh instructors list when instructor form is
/// not in edit, and there is no change to save
/// </summary>
public RelayCommand RefreshAllCommand
{
get
{
if (_refreshAllCommand == null)
{
_refreshAllCommand = new RelayCommand(
OnRefreshAllCommand,
() => !InstructorFormInEdit && (_schoolModel != null)
&& !(_schoolModel.InstructorsListHasChanges));
}
return _refreshAllCommand;
}
}
private RelayCommand _refreshAllCommand;
private void OnRefreshAllCommand()
{
if (!_schoolModel.IsBusy)
{
// refresh instructors
_schoolModel.GetInstructorsAsync("Courses", "InstructorPage");
}
}
/// <summary>
/// Submit change command. We can submit changes
/// when instructor form is not in edit, and there are changes to save
/// </summary>
public RelayCommand SubmitAllChangeCommand
{
get
{
if (_submitAllChangeCommand == null)
{
_submitAllChangeCommand = new RelayCommand(
OnSubmitAllChangeCommand,
() => !InstructorFormInEdit && (_schoolModel != null)
&& (_schoolModel.InstructorsListHasChanges));
}
return _submitAllChangeCommand;
}
}
private RelayCommand _submitAllChangeCommand;
private void OnSubmitAllChangeCommand()
{
try
{
if (!_schoolModel.IsBusy)
{
if (AllInstructors != null)
{
// we only save changes when all instructors passed validation
var passedValidation = AllInstructors.All(o => o.TryValidateObjectGraph());
if (!passedValidation) return;
_schoolModel.SaveInstructorChangesAsync();
}
}
}
catch (Exception ex)
{
// notify user if there is any error
AppMessages.RaiseErrorMessage.Send(ex);
}
}
/// <summary>
/// Cancel change command. We can cancel changes
/// when instructor form is not in edit, and there are changes to cancel
/// </summary>
public RelayCommand CancelAllChangeCommand
{
get
{
if (_cancelAllChangeCommand == null)
{
_cancelAllChangeCommand = new RelayCommand(
OnCancelAllChangeCommand,
() => !InstructorFormInEdit && (_schoolModel != null)
&& (_schoolModel.InstructorsListHasChanges));
}
return _cancelAllChangeCommand;
}
}
private RelayCommand _cancelAllChangeCommand;
private void OnCancelAllChangeCommand()
{
try
{
if (!_schoolModel.IsBusy)
{
// ask to confirm canceling changes
DialogMessage dialogMessage = new DialogMessage(
this,
ApplicationStrings.CancelAnyChangesMessageBoxText,
s =>
{
if (s == MessageBoxResult.OK)
{
// if confirmed, cancel changes for InstructorsList
_schoolModel.RejectInstructorChanges();
}
})
{
Button = MessageBoxButton.OKCancel,
Caption = ApplicationStrings.ConfirmMessageBoxCaption
};
AppMessages.PleaseConfirmMessage.Send(dialogMessage);
}
}
catch (Exception ex)
{
// notify user if there is any error
AppMessages.RaiseErrorMessage.Send(ex);
}
}
#endregion "Public Commands"
#region "Private Methods"
private void InstructorFormBeginEdit()
{
if (CurrentInstructor != null)
{
CurrentInstructor.BeginEdit();
AppMessages.BeginEditMessage.Send("Instructor");
InstructorFormInEdit = true;
}
}
private void InstructorFormEndEdit()
{
AppMessages.EndEditMessage.Send("Instructor");
InstructorFormInEdit = false;
if (CurrentInstructor != null) CurrentInstructor.EndEdit();
}
private void InstructorFormCancelEdit()
{
if (CurrentInstructor != null)
{
AppMessages.CancelEditMessage.Send("Instructor");
InstructorFormInEdit = false;
CurrentInstructor.CancelEdit();
}
}
/// <summary>
/// Event handler for GetInstructorsCompleted
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void _schoolModel_GetInstructorsCompleted(object sender, ResultsArgs<Instructor> e)
{
var screenName = e.UserState as string;
if (!string.Equals(screenName, "InstructorPage")) return;
if (!e.HasError)
{
// clear any previous error after a successful call
_schoolModel.ClearLastError();
// cancel any changes before setting AllInstructorsSource
if (_schoolModel.InstructorsListHasChanges)
{
_schoolModel.RejectInstructorChanges();
}
AllInstructors = new ObservableCollection<Instructor>(e.Results);
AllInstructorsSource = new CollectionViewSource { Source = AllInstructors };
// sort by instructor Id
AllInstructorsSource.SortDescriptions.Add(new SortDescription("PersonId", ListSortDirection.Ascending));
AllInstructorsSource.View.CurrentChanged += AllInstructorsSourceView_CurrentChanged;
if (AllInstructors.Count >= 1)
{
if (CurrentInstructor != null)
{
var currentInstructor = AllInstructors
.FirstOrDefault(n => n.PersonId == CurrentInstructor.PersonId);
if (currentInstructor != null)
AllInstructorsSource.View.MoveCurrentTo(currentInstructor);
else
// set the first row as the current instructor in edit
AllInstructorsSource.View.MoveCurrentToFirst();
}
else
{
// set the first row as the current instructor in edit
AllInstructorsSource.View.MoveCurrentToFirst();
}
CurrentInstructor = (Instructor)AllInstructorsSource.View.CurrentItem;
}
else
{
CurrentInstructor = null;
}
}
else
{
// notify user if there is any error
AppMessages.RaiseErrorMessage.Send(e.Error);
}
}
/// <summary>
/// Event handler for CurrentChanged
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void AllInstructorsSourceView_CurrentChanged(object sender, EventArgs e)
{
CurrentInstructor = (Instructor) AllInstructorsSource.View.CurrentItem;
}
/// <summary>
/// Event handler for GetInstructorByIdCompleted
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void _schoolModel_GetInstructorByIdCompleted(object sender, ResultArgs<Instructor> e)
{
if (!e.HasError)
{
// clear any previous error after a successful call
_schoolModel.ClearLastError();
Instructor currentInstructor = e.Results;
if (currentInstructor != null)
{
// find the matching current instructor from AllInstructors list
Instructor matchedInstructor = AllInstructors.Single(n => n.PersonId == currentInstructor.PersonId);
// replace matchedInstructor from AllInstructors list with what is returned from GetInstructorById
int index = AllInstructors.IndexOf(matchedInstructor);
AllInstructors.Remove(matchedInstructor);
AllInstructors.Insert(index, currentInstructor);
AllInstructorsSource.View.MoveCurrentTo(currentInstructor);
}
else
{
DialogMessage dialogMessage = new DialogMessage(
this,
ErrorResources.CurrentInstructorDoesNotExistMessageBoxText,
null)
{
Button = MessageBoxButton.OK,
Caption = ApplicationStrings.WarningMessageBoxCaption
};
AppMessages.StatusUpdateMessage.Send(dialogMessage);
}
}
else
{
// notify user if there is any error
AppMessages.RaiseErrorMessage.Send(e.Error);
}
}
/// <summary>
/// Event handler for SaveInstructorChangesCompleted
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void _schoolModel_SaveInstructorChangesCompleted(object sender, ResultArgs<string> e)
{
if (!e.HasError)
{
// clear any previous error after a successful call
_schoolModel.ClearLastError();
// check whether there is any warning message returned
if (!string.IsNullOrEmpty(e.Results))
{
DialogMessage dialogMessage = new DialogMessage(
this,
e.Results,
null)
{
Button = MessageBoxButton.OK,
Caption = ApplicationStrings.WarningMessageBoxCaption
};
AppMessages.StatusUpdateMessage.Send(dialogMessage);
// If last operation is a delete operation, call reject changes
if (CurrentInstructor != null && CurrentInstructor.ChangeTracker.State == ObjectState.Deleted)
{
_schoolModel.RejectInstructorChanges(false);
}
}
else
{
if (_schoolModel.InstructorsListHasChanges)
{
if (!_schoolModel.CurrentInstructorHasChanges)
// only refresh current instructor after Insert/Update/Delete
OnRefreshInstructorCommand();
}
else
// refresh the InstructorsList after Insert/Update/Delete
OnRefreshAllCommand();
}
}
else
{
// notify user if there is any error
AppMessages.RaiseErrorMessage.Send(e.Error);
}
}
/// <summary>
/// Event handler for PropertyChanged
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void _schoolModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
switch (e.PropertyName)
{
case "InstructorsListHasChanges":
RefreshAllCommand.RaiseCanExecuteChanged();
SubmitAllChangeCommand.RaiseCanExecuteChanged();
CancelAllChangeCommand.RaiseCanExecuteChanged();
break;
case "CurrentInstructorHasChanges":
RefreshInstructorCommand.RaiseCanExecuteChanged();
SubmitInstructorChangeCommand.RaiseCanExecuteChanged();
CancelInstructorChangeCommand.RaiseCanExecuteChanged();
break;
}
}
/// <summary>
/// Event handler for PropertyChanged
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void InstructorPageViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
switch (e.PropertyName)
{
case "CurrentInstructor":
DeleteInstructorCommand.RaiseCanExecuteChanged();
EditCommitInstructorCommand.RaiseCanExecuteChanged();
CancelEditInstructorCommand.RaiseCanExecuteChanged();
break;
case "InstructorFormInEdit":
InstructorListIsEnabled = !InstructorFormInEdit;
AddInstructorCommand.RaiseCanExecuteChanged();
DeleteInstructorCommand.RaiseCanExecuteChanged();
CancelEditInstructorCommand.RaiseCanExecuteChanged();
RefreshInstructorCommand.RaiseCanExecuteChanged();
SubmitInstructorChangeCommand.RaiseCanExecuteChanged();
CancelInstructorChangeCommand.RaiseCanExecuteChanged();
RefreshAllCommand.RaiseCanExecuteChanged();
SubmitAllChangeCommand.RaiseCanExecuteChanged();
CancelAllChangeCommand.RaiseCanExecuteChanged();
break;
}
}
#endregion "Private Methods"
}
}