//===============================================================================
// Goal Book.
// Copyright © 2009 Mark Brownsword.
//===============================================================================
#region Using Statements
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Windows;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using Csla;
using GoalBook.Infrastructure;
using GoalBook.Infrastructure.Constants;
using GoalBook.Infrastructure.Enums;
using GoalBook.Infrastructure.Events;
using GoalBook.Infrastructure.Interfaces;
using GoalBook.Infrastructure.ObjectModel;
using Infragistics.Windows.DataPresenter;
using Microsoft.Practices.Composite.Events;
using Microsoft.Practices.Composite.Presentation.Commands;
using Microsoft.Practices.Composite.Presentation.Events;
#endregion
namespace GoalBook.Goals.Views
{
/// <summary>
/// GoalsView Presenter.
/// </summary>
public class GoalsViewPresenter
{
#region Constants and Enums
#endregion
#region Inner Classes and Structures
#endregion
#region Delegates and Events
private DelegateCommand<CommandInfo> _newCommand;
private DelegateCommand<CommandInfo> _printCommand;
private DelegateCommand<CommandInfo> _undoCommand;
private DelegateCommand<CommandInfo> _deleteCommand;
private DelegateCommand<string> _searchCommand;
private DelegateCommand<string> _clearCommand;
#endregion
#region Instance and Shared Fields
private readonly INavigationService _navigationService;
private readonly IDialogService _dialogService;
private readonly ILoggerService _loggerService;
private readonly IPersistenceService _persistenceService;
private readonly IEventAggregator _eventAggregator;
private readonly IPrintService _printService;
private bool _syncInProgress = false;
#endregion
#region Constructors
/// <summary>
/// Constructor.
/// </summary>
public GoalsViewPresenter(INavigationService navigationService, IDialogService dialogService, ILoggerService loggerService,
IPersistenceService persistenceService, IEventAggregator eventAggregator, IPrintService printService)
{
//Set references.
_dialogService = dialogService;
_navigationService = navigationService;
_loggerService = loggerService;
_persistenceService = persistenceService;
_eventAggregator = eventAggregator;
_printService = printService;
//Initialise Model.
this.Model = new GoalsViewPresentationModel();
this.Model.Goals = new FilteredBindingList<Goal>(_persistenceService.Goals);
this.Model.LevelList = InitLevelList();
this.Model.Goals.ListChanged += Goals_ListChanged;
//Subscribe to events.
_persistenceService.SaveOccured += PersistenceService_SaveOccured;
_eventAggregator.GetEvent<SyncBeginEvent>().Subscribe(SyncBegin_EventHandler, ThreadOption.UIThread, true);
_eventAggregator.GetEvent<SyncEndEvent>().Subscribe(SyncEnd_EventHandler, ThreadOption.UIThread, true);
//Initialise ReferenceData.
ReferenceData.LevelList = _persistenceService.FetchLevelList();
ReferenceData.ContributesList = _persistenceService.FetchContributesList();
//Initialise Composite Commands. Note that these commands are active aware
//and the handlers will only execute when this view is the currently active view.
_printCommand = new DelegateCommand<CommandInfo>(PrintExecute_EventHandler, CanPrintExecute_EventHandler);
_undoCommand = new DelegateCommand<CommandInfo>(UndoExecute_EventHandler, CanUndoExecute_EventHandler);
_deleteCommand = new DelegateCommand<CommandInfo>(DeleteExecute_EventHandler, CanDeleteExecute_EventHandler);
_searchCommand = new DelegateCommand<string>(SearchExecute_EventHandler, CanSearchExecute_EventHandler);
_clearCommand = new DelegateCommand<string>(ClearExecute_EventHandler, CanClearExecute_EventHandler);
//Register the commands.
_navigationService.RegisterInfoCommand(GlobalCommandType.Print, _printCommand);
_navigationService.RegisterInfoCommand(GlobalCommandType.Undo, _undoCommand);
_navigationService.RegisterInfoCommand(GlobalCommandType.Delete, _deleteCommand);
_navigationService.RegisterStringCommand(GlobalCommandType.Search, _searchCommand);
_navigationService.RegisterStringCommand(GlobalCommandType.Clear, _clearCommand);
//NB: Remember to set new commands IsActive property in the View_IsActiveChanged event.
}
#endregion
#region Properties
/// <summary>
/// Reference to Model.
/// </summary>
public GoalsViewPresentationModel Model { get; set; }
/// <summary>
/// Reference to View.
/// </summary>
public GoalsView View { get; set; }
/// <summary>
/// Reference to ActionView.
/// </summary>
public GoalsActionView ActionView { get; set; }
#endregion
#region Private and Protected Methods
/// <summary>
/// Init LevelList for ActionView.
/// </summary>
/// <returns>Dictionary containing LevelList</returns>
private KeyValueItemList InitLevelList()
{
KeyValueItemList list = new KeyValueItemList();
foreach (KeyValuePair<int, string> pair in _persistenceService.FetchLevelList())
{
list.Add(new KeyValueItem(pair.Key.ToString(), pair.Value, 0));
}
return list;
}
/// <summary>
/// Initialise EditAction.
/// </summary>
private void InitialiseEditAction(EditActionEventArgs e)
{
SelectedRecordCollection records = e.Records;
if (records.Count > 0 && records[records.Count - 1] is DataRecord)
{
//Edit the last item in the collection. That is the one with the row selector applied.
DataRecord record = records[records.Count - 1] as DataRecord;
if (record.DataItem is Goal)
{
//Initialise DialogOptions.
DialogOptions options = new DialogOptions();
options.AllowResize = false;
options.DialogTitle = Properties.Resources.EditGoal;
options.IconUri = new Uri(MenuConstants.MENU_NEW_GOAL_IMAGE_URI);
options.Buttons = ButtonType.OKCancel;
this.PerformEditAction(options, (record.DataItem as Goal).Clone());
this.ApplyLevelFilter();
}
}
}
/// <summary>
/// Perform DeleteAction.
/// </summary>
private void PerformDeleteAction(SelectedRecordCollection records)
{
Goal goal;
foreach (DataRecord record in records)
{
goal = (record.DataItem as Goal);
// Initialise DialogOptions.
DialogOptions options = new DialogOptions();
options.AllowResize = false;
options.DialogTitle = Properties.Resources.DeleteGoalText;
options.DialogMessage = Properties.Resources.DeleteGoalMessageText;
options.IconUri = new Uri(MenuConstants.MESSAGE_ERROR_IMAGE_URI);
options.Buttons = ButtonType.YesNo;
if (this._dialogService.ShowMessageDialog(options) == true)
{
_persistenceService.Goals.BeginEdit();
_persistenceService.Goals.Remove(goal);
this.ValidateGoal(goal);
_persistenceService.UpdateContributesList(goal.GoalID);
}
}
}
/// <summary>
/// Perform EditAction.
/// </summary>
private void PerformEditAction(DialogOptions options, Goal goal)
{
//Initialise Presenter, Model and View.
GoalsDialogViewPresenter presenter = new GoalsDialogViewPresenter(goal, _persistenceService);
//Initialise View.
presenter.View = new GoalsDialogView();
presenter.InitView();
//Display the dialog.
bool? result = (_dialogService.ShowView(presenter.View, options,
presenter.Model.EditGoal as IDataErrorInfo));
if (result == true)
{
if (goal.IsSavable)
{
Mouse.SetCursor(Cursors.Wait);
//Locate the goal in the list.
for (int i = 0; i < Model.Goals.Count; i++)
{
if (Model.Goals[i].GoalID == goal.GoalID)
{
_persistenceService.Goals.BeginEdit();
//Apply the changes.
this.Model.Goals[i].MapFields(goal);
//Refresh the ActiveRecord - required if the grouping (LevelID) has changed.
this.View.RefreshSortPosition(true);
break;
}
}
}
}
}
/// <summary>
/// AddGoal.
/// </summary>
private void PerformAddAction(DialogOptions options, Goal goal)
{
//Initialise Presenter, Model and View.
GoalsDialogViewPresenter presenter = new GoalsDialogViewPresenter(goal, _persistenceService);
//Initialise View.
presenter.View = new GoalsDialogView();
presenter.InitView();
//Display the dialog.
bool? result = (_dialogService.ShowView(presenter.View, options,
presenter.Model.EditGoal as IDataErrorInfo));
if (result == true)
{
if (presenter.Model.EditGoal.IsSavable)
{
Mouse.SetCursor(Cursors.Wait);
_persistenceService.Goals.BeginEdit();
//Add the pending item to the list.
_persistenceService.Goals.Add(presenter.Model.EditGoal);
_persistenceService.Goals.EndNew(Model.Goals.IndexOf(presenter.Model.EditGoal));
// Refresh Sort order.
this.View.SetItemActive(presenter.Model.EditGoal);
this.View.RefreshSortPosition(true);
}
}
}
/// <summary>
/// Validate Goal.
/// </summary>
private bool ValidateGoal(Goal goal)
{
//Validate ContributesID and LevelID.
bool isValid = _persistenceService.Goals.ValidateRefIntegrity(goal) &&
_persistenceService.FetchLevelList().ContainsKey(goal.LevelID);
return isValid;
}
/// <summary>
/// Raise NewCanExecuteChanged.
/// </summary>
private void RaiseNewCanExecuteChanged()
{
_newCommand.RaiseCanExecuteChanged();
}
/// <summary>
/// Raise PrintCanExecuteChanged.
/// </summary>
private void RaisePrintCanExecuteChanged()
{
_printCommand.RaiseCanExecuteChanged();
}
/// <summary>
/// Raise UndoCanExecuteChanged.
/// </summary>
private void RaiseUndoCanExecuteChanged()
{
_undoCommand.RaiseCanExecuteChanged();
}
/// <summary>
/// Raise DeleteCanExecuteChanged.
/// </summary>
private void RaiseDeleteCanExecuteChanged()
{
_deleteCommand.RaiseCanExecuteChanged();
}
/// <summary>
/// Raise SearchCanExecuteChanged.
/// </summary>
private void RaiseSearchCanExecuteChanged()
{
_searchCommand.RaiseCanExecuteChanged();
}
/// <summary>
/// Raise ClearCanExecuteChanged.
/// </summary>
private void RaiseClearCanExecuteChanged()
{
_clearCommand.RaiseCanExecuteChanged();
}
/// <summary>
/// GetMenuInfo. Returns all menuinfo instances for the module.
/// </summary>
private MenuInfo[] GetMenuInfo()
{
List<MenuInfo> list = new List<MenuInfo>();
//list.Add(GetNewMenuInfo());
return list.ToArray();
}
/// <summary>
/// Get New MenuInfo.
/// </summary>
public MenuInfo GetNewMenuInfo()
{
MenuInfo menuInfo = new MenuInfo();
_newCommand = new DelegateCommand<CommandInfo>(NewExecute_EventHandler, CanNewExecute_EventHandler);
menuInfo.MenuDelegateCommand = _newCommand;
menuInfo.MenuCommandInfo = new CommandInfo(
Properties.Resources.NewGoalMenuText,
0,
ParentMenuType.New,
new Uri(MenuConstants.MENU_NEW_GOAL_IMAGE_URI),
new KeyGesture(Key.G, ModifierKeys.Control | ModifierKeys.Shift, Properties.Resources.ModifierKeyControlShiftG));
return menuInfo;
}
/// <summary>
/// Reset the List. Sorts item as required.
/// </summary>
private void ResetList()
{
//Refresh all list items.
for (int i = 0; i < Model.Goals.Count; i++)
{
_persistenceService.Goals.ResetItem(i);
}
}
/// <summary>
/// Select Active Record.
/// </summary>
private void SelectActiveRecord()
{
if (this.View.xamDataGridGoals.ActiveRecord != null)
{
this.View.xamDataGridGoals.Focus();
this.View.xamDataGridGoals.ActiveRecord.IsSelected = true;
}
}
/// <summary>
/// Apply Title Filter.
/// </summary>
/// <param name="parameter"></param>
private void ApplyTitleFilter(string parameter)
{
this.Model.Goals.FilterProvider = TitleFilter;
this.Model.Goals.ApplyFilter(Goal.IdProperty.Name, parameter);
}
/// <summary>
/// Title Filter.
/// </summary>
/// <param name="item">item parameter</param>
/// <param name="filter">filter parameter</param>
/// <returns>True if filter item found</returns>
private bool TitleFilter(object item, object filter)
{
bool result = false;
Guid id = new Guid(item.ToString());
Goal target = this._persistenceService.Goals.Single(goal => goal.GoalID == id);
if (target.Title.Contains(filter.ToString()) &&
this.LevelFilter(target.LevelID, this.ActionView.Filter))
{
result = true;
}
return result;
}
/// <summary>
/// Apply Level Filter.
/// </summary>
private void ApplyLevelFilter()
{
if (this.ActionView.Filter != null)
{
this.Model.Goals.FilterProvider = this.LevelFilter;
this.Model.Goals.ApplyFilter(Goal.LevelProperty.Name, this.ActionView.Filter);
}
}
/// <summary>
/// Level Filter.
/// </summary>
/// <param name="item">item parameter</param>
/// <param name="filter">filter parameter</param>
/// <returns>True if filter item found</returns>
private bool LevelFilter(object item, object filter)
{
string[] filterSplit = filter.ToString().Split(',');
for (int i = 0; i < filterSplit.Length; i++)
{
if (filterSplit[i] == item.ToString())
{
return true;
}
}
return false;
}
/// <summary>
/// Show Status Count Message.
/// </summary>
private void ShowStatusCountMessage()
{
if (this.View.IsActive)
{
_navigationService.ShowStatusMessage(string.Format(
Properties.Resources.StatusCountMessage,
this.Model.Goals.Count,
this.Model.Goals.Count == 1 ? string.Empty : Properties.Resources.StatusCountPluralCharacter,
_persistenceService.Goals.Count));
}
}
#endregion
#region Public and internal Methods
/// <summary>
/// Initialise Views.
/// </summary>
public void InitViews()
{
this.View.DataContext = this.Model;
this.View.GoalSelected += new EventHandler(this.View_GoalSelected);
this.View.IsActiveChanged += new EventHandler(this.View_IsActiveChanged);
this.View.Activated += new EventHandler(this.View_Activated);
this.View.EditActionInitiated += new EditActionEventHandler(this.View_EditActionInitiated);
this.ActionView.DataContext = this.Model;
this.ActionView.Filter = null;
this.ActionView.FilterChanged += new EventHandler(this.ActionView_FilterChanged);
this.ApplyLevelFilter();
}
/// <summary>
/// Reference to ViewMenuInfo. Called by GoalsMoldule
/// each time the this view is activated.
/// </summary>
public MenuInfo[] ViewMenuInfo
{
get { return GetMenuInfo(); }
}
#endregion
#region Event Handlers
/// <summary>
/// Handle SyncBegin_EventHandler event.
/// </summary>
private void SyncBegin_EventHandler(string payload)
{
this.NotifyCommandSyncInProgress(true);
this.ActionView.IsEnabled = false;
}
/// <summary>
/// Handle SyncEnd_EventHandler event.
/// </summary>
private void SyncEnd_EventHandler(string payload)
{
this.ResetList();
this.ActionView.IsEnabled = true;
this.NotifyCommandSyncInProgress(false);
}
/// <summary>
/// Notify Command SyncInProgress. Ensure menuitems for editing
/// goals are disabled during synchronisation.
/// </summary>
private void NotifyCommandSyncInProgress(bool syncInProgress)
{
_syncInProgress = syncInProgress;
this.RaiseNewCanExecuteChanged();
this.RaisePrintCanExecuteChanged();
this.RaiseUndoCanExecuteChanged();
this.RaiseDeleteCanExecuteChanged();
this.RaiseSearchCanExecuteChanged();
this.RaiseClearCanExecuteChanged();
}
/// <summary>
/// Handle View_EditActionInitiated event.
/// </summary>
private void View_EditActionInitiated(EditActionEventArgs e)
{
if (_syncInProgress) { return; }
switch (e.EditAction)
{
case EditActionType.Cut:
break;
case EditActionType.Copy:
break;
case EditActionType.Paste:
break;
case EditActionType.Delete:
break;
case EditActionType.Edit:
this.InitialiseEditAction(e);
break;
}
}
/// <summary>
/// Handle View_GoalSelected event.
/// </summary>
private void View_GoalSelected(object sender, EventArgs e)
{
this.RaiseDeleteCanExecuteChanged();
}
/// <summary>
/// Handle Goals_ListChanged event.
/// </summary>
private void Goals_ListChanged(object sender, System.ComponentModel.ListChangedEventArgs e)
{
this.RaisePrintCanExecuteChanged();
this.RaiseUndoCanExecuteChanged();
this.RaiseDeleteCanExecuteChanged();
this.RaiseSearchCanExecuteChanged();
this.RaiseClearCanExecuteChanged();
this.ShowStatusCountMessage();
}
/// <summary>
/// Handle View_IsActiveChanged event.
/// </summary>
private void View_IsActiveChanged(object sender, EventArgs e)
{
_printCommand.IsActive =
_undoCommand.IsActive =
_deleteCommand.IsActive =
_searchCommand.IsActive =
_clearCommand.IsActive = this.View.IsActive;
this.ShowStatusCountMessage();
}
/// <summary>
/// Handle View_Activated event.
/// </summary>
private void View_Activated(object sender, EventArgs e)
{
if (this.View.xamDataGridGoals.ActiveRecord != null)
{
this.View.xamDataGridGoals.Focus();
this.View.xamDataGridGoals.ActiveRecord.IsSelected = true;
}
}
/// <summary>
/// Handle PersistenceService_SaveOccured event.
/// </summary>
private void PersistenceService_SaveOccured(object sender, EventArgs e)
{
this.RaiseUndoCanExecuteChanged();
}
/// <summary>
/// Handle PrintExecute_EventHandler event.
/// </summary>
/// <param name="parameter">CommandInfo parameter</param>
private void PrintExecute_EventHandler(CommandInfo parameter)
{
// Initialise the FlowDocument.
FlowDocument document = new FlowDocument();
// Initialise Table.
Table goalTable = new Table();
goalTable.CellSpacing = 15;
goalTable.Background = Brushes.White;
goalTable.Columns.Add(new TableColumn());
goalTable.Columns.Add(new TableColumn());
goalTable.Columns[1].Width = new GridLength(75, GridUnitType.Pixel);
goalTable.Columns[1].Background = Brushes.Gainsboro;
goalTable.RowGroups.Add(new TableRowGroup());
// Row counter.
int rowCounter = -1;
//Initialise Column Title Row.
goalTable.RowGroups[0].Rows.Add(new TableRow());
TableRow currentRow = goalTable.RowGroups[0].Rows[++rowCounter];
currentRow.Background = Brushes.Gainsboro;
currentRow.FontFamily = new FontFamily(PrintConstants.FONT_FAMILY_ARIAL);
currentRow.FontSize = 14;
currentRow.FontWeight = FontWeights.Bold;
currentRow.Cells.Add(new TableCell(new Paragraph(new Run(Properties.Resources.LabelTitle))));
currentRow.Cells.Add(new TableCell(new Paragraph(new Run(Properties.Resources.LabelLevel))));
// Initialise Rows (ordered by Level).
var orderedGoalList = from g in Model.Goals orderby g.LevelID select g;
foreach (Goal goal in orderedGoalList)
{
goalTable.RowGroups[0].Rows.Add(new TableRow());
currentRow = goalTable.RowGroups[0].Rows[++rowCounter];
currentRow.FontSize = 12;
currentRow.FontWeight = FontWeights.Normal;
currentRow.Cells.Add(new TableCell(new Paragraph(new Run(goal.ToString()))));
currentRow.Cells.Add(new TableCell(new Paragraph(new Run(_persistenceService.FetchLevelList()[goal.LevelID].ToString()))));
}
// Add the table to the FlowDocument.
document.Blocks.Add(goalTable);
// Pass the FlowDocument to PrintService.
_printService.Print(Properties.Resources.LabelGoals, document);
}
/// <summary>
/// Handle CanPrintExecute_EventHandler event.
/// </summary>
/// <param name="parameter">CommandInfo parameter</param>
private bool CanPrintExecute_EventHandler(CommandInfo parameter)
{
return _persistenceService.Goals.Count > 0 && !_syncInProgress;
}
/// <summary>
/// Handle UndoExecute_EventHandler event.
/// </summary>
/// <param name="parameter">CommandInfo parameter</param>
private void UndoExecute_EventHandler(CommandInfo parameter)
{
//Roll the EditLevel back by one.
_persistenceService.Goals.CancelEdit();
ResetList();
}
/// <summary>
/// Handle CanUndoExecute_EventHandler event.
/// </summary>
/// <param name="parameter">CommandInfo parameter</param>
private bool CanUndoExecute_EventHandler(CommandInfo parameter)
{
return _persistenceService.Goals.EditLevel > 0 && !_syncInProgress;
}
/// <summary>
/// Handle DeleteExecute_EventHandler event.
/// </summary>
/// <param name="parameter">CommandInfo parameter</param>
private void DeleteExecute_EventHandler(CommandInfo parameter)
{
PerformDeleteAction(View.SelectedGoals);
ResetList();
SelectActiveRecord();
}
/// <summary>
/// Handle CanDeleteExecute_EventHandler event.
/// </summary>
/// <param name="parameter">string parameter</param>
private bool CanDeleteExecute_EventHandler(CommandInfo parameter)
{
return View.SelectedGoalsCount > 0 && !_syncInProgress;
}
/// <summary>
/// Handle SearchExecute_EventHandler event.
/// </summary>
/// <param name="parameter">string parameter</param>
private void SearchExecute_EventHandler(string parameter)
{
this.ApplyTitleFilter(parameter);
}
/// <summary>
/// Handle CanSearchExecute_EventHandler event.
/// </summary>
/// <param name="parameter">string parameter</param>
private bool CanSearchExecute_EventHandler(string parameter)
{
return !_syncInProgress;
}
/// <summary>
/// Handle ClearExecute_EventHandler event.
/// </summary>
/// <param name="parameter">string parameter</param>
private void ClearExecute_EventHandler(string parameter)
{
this.ApplyLevelFilter();
}
/// <summary>
/// Handle CanClearExecute_EventHandler event.
/// </summary>
/// <param name="parameter">string parameter</param>
private bool CanClearExecute_EventHandler(string parameter)
{
return !_syncInProgress;
}
/// <summary>
/// Handle ActionView_FilterChanged event.
/// </summary>
/// <param name="sender">sender parameter</param>
/// <param name="e">EventArgs parameter</param>
private void ActionView_FilterChanged(object sender, EventArgs e)
{
this.ApplyLevelFilter();
}
/// <summary>
/// Handle NewExecute_EventHandler event.
/// </summary>
/// <param name="parameter">CommandInfo parameter</param>
private void NewExecute_EventHandler(CommandInfo parameter)
{
DialogOptions options = new DialogOptions();
options.AllowResize = false;
options.DialogTitle = Properties.Resources.NewGoalDialogTitle;
options.IconUri = new Uri(MenuConstants.MENU_NEW_GOAL_IMAGE_URI);
options.Buttons = ButtonType.OKCancel;
Goal goal = new Goal(Guid.NewGuid());
PerformAddAction(options, goal);
ApplyLevelFilter();
//Refresh ContributesList (update the list with this new item).
_persistenceService.UpdateContributesList(goal.GoalID);
}
/// <summary>
/// Handle CanNewExecute_EventHandler event.
/// </summary>
/// <param name="parameter">CommandInfo parameter</param>
private bool CanNewExecute_EventHandler(CommandInfo parameter)
{
return !_syncInProgress;
}
#endregion
#region Base Class Overrides
#endregion
}
}