Click here to Skip to main content
15,860,844 members
Articles / Desktop Programming / WPF

GoalBook - A Hybrid Smart Client

Rate me:
Please Sign up or sign in to vote.
4.86/5 (24 votes)
25 Sep 2009CPOL10 min read 78.6K   834   69  
A WPF hybrid smart client that synchronises your goals with the Toodledo online To-do service.
//===============================================================================
// Goal Book.
// Copyright © 2009 Mark Brownsword. 
//===============================================================================

#region Using Statements
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using GoalBook.Infrastructure;
using GoalBook.Infrastructure.Constants;
using GoalBook.Infrastructure.Enums;
using GoalBook.Infrastructure.Events;
using GoalBook.Infrastructure.Interfaces;
using GoalBook.Shell.Misc;
using GoalBook.Shell.Services;
using GoalBook.Shell.Views;
using GoalBook.Synchronisation;
using GoalBook.Synchronisation.Events;
using Microsoft.Practices.Composite.Events;
using Microsoft.Practices.Composite.Logging;
using Microsoft.Practices.Composite.Presentation.Commands;
using Microsoft.Practices.Composite.Presentation.Events;
using Microsoft.Practices.Unity;
using ShellResources = GoalBook.Shell.Properties.Resources;
#endregion

namespace GoalBook.Shell.Windows
{
    public class MainPresenter
    {
        #region Constants and Enums
        #endregion

        #region Inner Classes and Structures
        #endregion

        #region Delegates and Events
        #region SyncBegin Event
        /// <summary>
        /// Raise the SyncBegin event.
        /// </summary>        
        private void OnSyncBegin()
        {
            _eventAggregator.GetEvent<SyncBeginEvent>().Publish(ShellResources.SyncBeginMessage);

            _syncActive = true;
            Model.SyncCommand.RaiseCanExecuteChanged();
            Model.ConfigureAccountCommand.RaiseCanExecuteChanged();            
            Model.InternetCommand.RaiseCanExecuteChanged();
        }
        #endregion
        #region SyncEnd Event
        /// <summary>
        /// Raise the SyncComplete event.
        /// </summary>        
        private void OnSyncEnd()
        {
            _eventAggregator.GetEvent<SyncEndEvent>().Publish(ShellResources.SyncEndMessage);

            _syncActive = false;

            Model.InternetCommand.RaiseCanExecuteChanged();
            Model.SyncCommand.RaiseCanExecuteChanged();
            Model.ConfigureAccountCommand.RaiseCanExecuteChanged();
            Model.ChangeAccountCommand.RaiseCanExecuteChanged();
            Model.ClearDataCommand.RaiseCanExecuteChanged();

            View.ShowStatusSyncImage(_persistenceService.SyncRequired);
        }
        #endregion
        #endregion

        #region Instance and Shared Fields
        private readonly IUnityContainer _unityContainer;
        private readonly IPersistenceService _persistenceService;
        private readonly ILoggerService _logger;
        private readonly IDialogService _dialogService;
        private readonly IEventAggregator _eventAggregator;
        private readonly ApplicationSettings _settingsService;
        private SyncService _syncService;
        private bool _syncActive = false;
        #endregion

        #region Constructors
        /// <summary>
        /// Constructor.
        /// </summary>
        public MainPresenter(IUnityContainer unityContainer)
        {
            //Set Container reference.
            _unityContainer = unityContainer;

            //Set the Persistence Service.
            _persistenceService = _unityContainer.Resolve<IPersistenceService>();
            _persistenceService.SaveOccured += new EventHandler(PersistenceService_SaveOccured);
            _persistenceService.Goals.ListChanged += new ListChangedEventHandler(PersistenceService_ListChanged);
            _persistenceService.Folders.ListChanged += new ListChangedEventHandler(PersistenceService_ListChanged);
            _persistenceService.Notes.ListChanged += new ListChangedEventHandler(PersistenceService_ListChanged);
            _persistenceService.Tasks.ListChanged += new ListChangedEventHandler(PersistenceService_ListChanged);

            //Set the ApplicationSettings Service.
            _settingsService = _unityContainer.Resolve<ISettingsService>() as ApplicationSettings;
            
            //Set Dialog Service.
            _dialogService = _unityContainer.Resolve<IDialogService>();

            //Set the logger.
            _logger = _unityContainer.Resolve<ILoggerService>();

            _eventAggregator = _unityContainer.Resolve<IEventAggregator>();
            _eventAggregator.GetEvent<PrintBeginEvent>().Subscribe(PrintBegin_EventHandler, ThreadOption.UIThread, true);
            _eventAggregator.GetEvent<PrintEndEvent>().Subscribe(PrintEnd_EventHandler, ThreadOption.UIThread, true);
            _eventAggregator.GetEvent<PrintExceptionEvent>().Subscribe(PrintException_EventHandler, ThreadOption.UIThread, true);

            //Initialise View.
            View = _unityContainer.Resolve<IShellView>();            
            View.CanClose += new CancelEventHandler(View_CanClose);
            View.SaveChanges += new EventHandler(View_SaveChanges);
            View.ShowStatusSyncImage(_persistenceService.SyncRequired);

            //Initialise Model.
            Model = _unityContainer.Resolve<MainPresentationModel>();
            Model.SaveCommand = new DelegateCommand<CommandInfo>(SaveExecute_EventHandler, CanSaveExecute_EventHandler);
            Model.FeedbackCommand = new DelegateCommand<CommandInfo>(FeedbackExecute_EventHandler);
            Model.AboutCommand = new DelegateCommand<CommandInfo>(AboutExecute_EventHandler);
            Model.SyncCommand = new DelegateCommand<CommandInfo>(SyncExecute_EventHandler, CanSyncExecute_EventHandler);
            Model.ConfigureAccountCommand = new DelegateCommand<CommandInfo>(ConfigureAccountExecute_EventHandler, CanConfigureAccountExecute_EventHandler);
            Model.ChangeAccountCommand = new DelegateCommand<CommandInfo>(ChangeAccountExecute_EventHandler, CanChangeAccountExecute_EventHandler);
            Model.ClearDataCommand = new DelegateCommand<CommandInfo>(ClearDataExecute_EventHandler, CanClearDataExecute_EventHandler);
            Model.InternetCommand = new DelegateCommand<CommandInfo>(InternetConnectionExecute_EventHandler, CanInternetConnectionExecute_EventHandler);
            Model.ExitCommand = new DelegateCommand<CommandInfo>(ExitExecute_EventHandler);

            //Initialise settings.
            if (_settingsService.ConnectInfo == null) { _settingsService.ConnectInfo = new Credentials(); }
            if (_settingsService.HostInfo == null) { _settingsService.HostInfo = new Proxy(); }
            if (_settingsService.TokenInfo == null) { _settingsService.TokenInfo = new SyncTokenInfo(); }            
        }           
        #endregion

        #region Properties
        /// <summary>
        /// Reference to Model.
        /// </summary>
        public MainPresentationModel Model { get; private set; }
        /// <summary>
        /// Reference to View.
        /// </summary>
        public IShellView View { get; private set; }
        #endregion

        #region EventHandlers        
        /// <summary>
        /// Handle View_SaveChanges event.
        /// </summary>        
        private void View_SaveChanges(object sender, EventArgs e)
        {
            Save();
        }
     
        /// <summary>
        /// Handle View_CanClose event.
        /// </summary>        
        private void View_CanClose(object sender, CancelEventArgs e)
        {
            if (_persistenceService.SaveRequired)
            {
                // Initialise DialogOptions.
                DialogOptions options = new DialogOptions();
                options.AllowResize = false;
                options.DialogTitle = Properties.Resources.SaveChangesCaption;
                options.DialogMessage = Properties.Resources.SaveChangesMessage;
                options.IconUri = new Uri(MenuConstants.MESSAGE_ERROR_IMAGE_URI);
                options.Buttons = ButtonType.YesNoCancel;

                bool? result = this._dialogService.ShowMessageDialog(options);
                if (result == true)
                {
                    Save();
                }
                else if (result == null)
                {
                    e.Cancel = true;
                }
            }            
        }

        /// <summary>
        /// Handle PersistenceService_SaveOccured event.
        /// </summary>
        /// <param name="sender">sender parameter</param>
        /// <param name="e">EventArgs parameter</param>
        private void PersistenceService_SaveOccured(object sender, EventArgs e)
        {
            View.ShowStatusMessage(Properties.Resources.DataSavedMessage);
        }

        /// <summary>
        /// Handle Goals_ListChanged event.
        /// </summary>        
        private void PersistenceService_ListChanged(object sender, ListChangedEventArgs e)
        {
            Model.SaveCommand.RaiseCanExecuteChanged();

            if (_persistenceService.SyncRequired)
            {
                View.ShowStatusMessage(string.Empty);
            }

            View.ShowStatusSyncImage(_persistenceService.SyncRequired);
        }

        /// <summary>
        /// Handle CanInternetConnectionExecute_EventHandler event.
        /// </summary>
        /// <param name="parameter">Command Info.</param>
        private bool CanInternetConnectionExecute_EventHandler(CommandInfo parameter)
        {
            if (_syncActive) { return false; }
            return true;
        }

        /// <summary>
        /// Handle InternetConnectionExecute_EventHandler event.
        /// </summary>
        /// <param name="parameter">Command Info.</param>
        private void InternetConnectionExecute_EventHandler(CommandInfo parameter)
        {
            Mouse.SetCursor(Cursors.Wait);

            InternetDialogViewPresenter presenter = new InternetDialogViewPresenter(_settingsService.HostInfo.Clone());
            IShellView shell = _unityContainer.Resolve<IShellView>();

            presenter.View = new InternetDialogView();            
            presenter.InitView();

            DialogOptions options = new DialogOptions();
            options.AllowResize = false;
            options.DialogTitle = parameter.Title.TrimEnd('.');
            options.IconUri = parameter.IconUri;
            options.Buttons = ButtonType.OKCancel;

            //Display the dialog.
            bool? result = (_dialogService.ShowView(presenter.View, options,
                presenter.Model.HostInfo as IDataErrorInfo));

            if (result == true)
            {
                _settingsService.HostInfo.MapFields(presenter.Model.HostInfo);
            }
        }

        /// <summary>
        /// Handle CanConfigureAccountExecute_EventHandler event.
        /// </summary>
        /// <param name="parameter">Command Info.</param>
        /// <returns>True if menuitem can execute.</returns>
        private bool CanConfigureAccountExecute_EventHandler(CommandInfo parameter)
        {
            if (_syncActive) { return false; }
            return true;
        }

        /// <summary>
        /// Handle ConfigureAccountExecute_EventHandler event.
        /// </summary>        
        private void ConfigureAccountExecute_EventHandler(CommandInfo parameter)
        {
            Mouse.SetCursor(Cursors.Wait);

            AccountDialogViewPresenter presenter = new AccountDialogViewPresenter(
                _settingsService.ConnectInfo.Clone(),
                string.IsNullOrEmpty(_settingsService.ConnectInfo.UserID) ? 
                Properties.Resources.ConfigureNewAccountInfoMessage : 
                Properties.Resources.ConfigureUpdateAccountInfoMessage);
                        
            presenter.View = new AccountDialogView();
            presenter.InitView();
            
            DialogOptions options = new DialogOptions();
            options.AllowResize = false;
            options.DialogTitle = parameter.Title.TrimEnd('.');
            options.IconUri = parameter.IconUri;
            options.Buttons = ButtonType.OKCancel;

            //Display the dialog.
            bool? result = (_dialogService.ShowView(presenter.View, options,
                presenter.Model.ConnectInfo as IDataErrorInfo));

            if (result == true)
            {
                _settingsService.ConnectInfo.MapFields(presenter.Model.ConnectInfo);
                Model.SyncCommand.RaiseCanExecuteChanged();                
            }
        }

        /// <summary>
        /// Handle CanChangeAccountExecute_EventHandler event.
        /// </summary>
        /// <param name="parameter">Command Info.</param>
        /// <returns>True if menuitem can execute.</returns>
        private bool CanChangeAccountExecute_EventHandler(CommandInfo parameter)
        {
            if (_syncActive) { return false; }
            return !string.IsNullOrEmpty(_settingsService.ConnectInfo.UserID);
        }

        /// <summary>
        /// Handle ChangeAccountExecute_EventHandler event.
        /// </summary>        
        private void ChangeAccountExecute_EventHandler(CommandInfo parameter)
        {
            Mouse.SetCursor(Cursors.Wait);
            
            AccountDialogViewPresenter presenter = new AccountDialogViewPresenter(new Credentials(), Properties.Resources.ChangeAccountMessage);
            presenter.View = new AccountDialogView();
            presenter.InitView();

            DialogOptions options = new DialogOptions();
            options.AllowResize = false;
            options.DialogTitle = parameter.Title.TrimEnd('.');
            options.IconUri = parameter.IconUri;
            options.Buttons = ButtonType.OKCancel;
            
            //Display the dialog.
            bool? result = (_dialogService.ShowView(
                presenter.View, options, 
                () =>
                {
                    bool? confirmResult = this._dialogService.ShowMessageDialog(GetClearDataDialogOptions());                    
                    return confirmResult.Value;
                }, 
                presenter.Model.ConnectInfo as IDataErrorInfo));

            if (result == true)
            {
                //Clear and save user data.
                _persistenceService.Clear();
                Save();

                // Clear and save user account settings.
                _settingsService.ConnectInfo.Clear();
                _settingsService.TokenInfo.Clear();
                _settingsService.ConnectInfo.MapFields(presenter.Model.ConnectInfo);
                _settingsService.Save();

                Model.ChangeAccountCommand.RaiseCanExecuteChanged();
                Model.ClearDataCommand.RaiseCanExecuteChanged();
                Model.SyncCommand.RaiseCanExecuteChanged();
                Model.SaveCommand.RaiseCanExecuteChanged();

                View.ShowStatusSyncImage(_persistenceService.SyncRequired);
                View.ShowStatusMessage(Properties.Resources.ReadyMessage);                
            }
        }
        
        /// <summary>
        /// Handle CanClearDataExecute_EventHandler event.
        /// </summary>        
        private bool CanClearDataExecute_EventHandler(CommandInfo parameter)
        {
            if (_syncActive) { return false; }
            return !string.IsNullOrEmpty(_settingsService.ConnectInfo.UserID);
        }

        /// <summary>
        /// Handle ClearDataExecute_EventHandler event.
        /// </summary>        
        private void ClearDataExecute_EventHandler(CommandInfo parameter)
        {            
            bool? result = this._dialogService.ShowMessageDialog(GetClearDataDialogOptions());
            if (result == true)
            {
                //Clear and save user data.
                _persistenceService.Clear();
                Save();
                
                // Clear and save user account settings.
                _settingsService.ConnectInfo.Clear();
                _settingsService.TokenInfo.Clear();
                _settingsService.Save();

                Model.ChangeAccountCommand.RaiseCanExecuteChanged();                
                Model.ClearDataCommand.RaiseCanExecuteChanged();
                Model.SyncCommand.RaiseCanExecuteChanged();
                Model.SaveCommand.RaiseCanExecuteChanged();

                View.ShowStatusSyncImage(_persistenceService.SyncRequired);
                View.ShowStatusMessage(Properties.Resources.ReadyMessage);
            }
        }

        /// <summary>
        /// Handle CanSyncExecute_EventHandler event.
        /// </summary>        
        private bool CanSyncExecute_EventHandler(CommandInfo parameter)
        {
            if (_syncActive) { return false; }
            return _settingsService.ConnectInfo.IsValid;
        }

        /// <summary>
        /// Handle SyncExecute_EventHandler event.
        /// </summary>        
        private void SyncExecute_EventHandler(CommandInfo parameter)
        {
            // Validate Lists.
            if (!_persistenceService.Goals.IsValid 
                || !_persistenceService.Folders.IsValid 
                || !_persistenceService.Notes.IsValid
                || !_persistenceService.Tasks.IsValid) 
            { 
                View.ShowStatusMessage(Properties.Resources.SyncInvalidListMessage); 
                return; 
            }

            // Initialise SyncService.
            if (_syncService == null)
            {
                _syncService = new SyncService();
                _syncService.ConnectInfo = _settingsService.ConnectInfo;                                
                _syncService.HostInfo = _settingsService.HostInfo;
                _syncService.TokenInfo = _settingsService.TokenInfo;                
                _syncService.SyncProgressChanged += SyncService_SyncProgressChanged;
                _syncService.SyncCompleted += SyncService_SyncCompleted;
                _syncService.SyncException += SyncService_SyncException;                
            }
                        
            //Save before synchronisation.
            if (_persistenceService.Goals.IsSavable 
                || _persistenceService.Folders.IsSavable 
                || _persistenceService.Notes.IsSavable
                || _persistenceService.Tasks.IsSavable) 
            { 
                Save(); 
            }

            //Notify listeners that synchronisation has begun.
            OnSyncBegin();
            
            SyncData syncData = new SyncData();
            syncData.Goals = _persistenceService.Goals.Clone();
            syncData.Folders = _persistenceService.Folders.Clone();
            syncData.Notes = _persistenceService.Notes.Clone();
            syncData.Tasks = _persistenceService.Tasks.Clone();
                                            
            //Begin Synchronisation Asynchronously.
            _syncService.BeginSync(syncData);            
        }

        /// <summary>
        /// Handle SyncService_SyncProgressChanged event.
        /// </summary>        
        private void SyncService_SyncProgressChanged(SyncProgressChangedEventArgs e)
        {            
            View.ShowStatusMessage(e.Message);            
        }

        /// <summary>
        /// Handle SyncService_SyncCompleted event.
        /// </summary>        
        private void SyncService_SyncCompleted(SyncCompletedEventArgs e)
        {      
            //Merge changes.
            lock (_persistenceService.Goals) { _persistenceService.Goals.Merge(e.Data.Goals); }
            lock (_persistenceService.Folders) { _persistenceService.Folders.Merge(e.Data.Folders); }
            lock (_persistenceService.Notes) { _persistenceService.Notes.Merge(e.Data.Notes); }
            lock (_persistenceService.Tasks) { _persistenceService.Tasks.Merge(e.Data.Tasks); }

            // Refresh Folder List.
            _persistenceService.UpdateFolderList();
                                                            
            // Persist user settings (UserID gets updated if it is initially null).
            _settingsService.ConnectInfo.MapFields(_syncService.ConnectInfo);
            _settingsService.TokenInfo = _syncService.TokenInfo;
            _settingsService.Save();
                                                                        
            // Notify listeners that synchronisation has completed.
            OnSyncEnd();

            // Save after synchronisation.
            Save();

            // Update status message.
            ////View.ShowStatusMessage(e.StatusMessage);
        }

        /// <summary>
        /// Handle SyncService_SyncException event.
        /// </summary>        
        private void SyncService_SyncException(SyncExceptionEventArgs e)
        {            
            View.ShowStatusMessage(e.SyncException.Message);
            
            _logger.Log(_logger.GetExceptionMessage(e.SyncException), Category.Exception, Priority.High);

            //Notify listeners that synchronisation has completed.
            OnSyncEnd();
        }

        /// <summary>
        /// Handle FeedbackExecute_EventHandler event.
        /// </summary>
        /// <param name="parameter"></param>
        private void FeedbackExecute_EventHandler(CommandInfo parameter)
        {           
            Process p = new Process();
            p.StartInfo.FileName = Properties.Resources.FeedbackMailAddressText;
            p.StartInfo.UseShellExecute = true;
            p.StartInfo.RedirectStandardOutput = false;
            p.StartInfo.Arguments = string.Empty;

            p.Start();                
        }

        /// <summary>
        /// Handle AboutExecuteHandler event.
        /// </summary>
        private void AboutExecute_EventHandler(CommandInfo parameter)
        {
            Mouse.SetCursor(Cursors.Wait);                  

            AboutView view = _unityContainer.Resolve<AboutView>();
            IShellView shell = _unityContainer.Resolve<IShellView>();

            DialogOptions options = new DialogOptions();
            options.AllowResize = false;
            options.DialogTitle = Properties.Resources.MenuAboutText;
            options.IconUri = null;
            options.Buttons = ButtonType.OK;

            DialogPresenter dialogPresenter = new DialogPresenter();
            dialogPresenter.ShowView(shell as Window, view, options);
        }

        /// <summary>
        /// Handle ExitExecute_EventHandler event.
        /// </summary>        
        private void ExitExecute_EventHandler(CommandInfo parameter)
        {            
            View.ExitView();
        }

        /// <summary>
        /// Handle SaveExecute_EventHandler event.
        /// </summary>        
        private void SaveExecute_EventHandler(CommandInfo parameter)
        {
            Mouse.SetCursor(Cursors.Wait);
            try
            {
                Save();                
            }
            finally { Mouse.UpdateCursor(); }
        } 
       
        /// <summary>
        /// Handle CanSaveExecute_EventHandler event.
        /// </summary>        
        private bool CanSaveExecute_EventHandler(CommandInfo parameter)
        {
            return _persistenceService.SaveRequired;
        }

        /// <summary>
        /// Handle PrintBegin_EventHandler event.
        /// </summary>
        /// <param name="message">Status message from Print Service</param>
        private void PrintBegin_EventHandler(string message)
        {
            View.ShowStatusMessage(message);
        }

        /// <summary>
        /// Handle PrintEnd_EventHandler event.
        /// </summary>
        /// <param name="message">Status message from Print Service</param>
        private void PrintEnd_EventHandler(string message)
        {
            View.ShowStatusMessage(message);
        }

        /// <summary>
        /// Handle PrintException_EventHandler event.
        /// </summary>
        /// <param name="ex">Exception from Print Service</param>
        private void PrintException_EventHandler(PrintDialogException ex)
        {
            View.ShowStatusMessage(ex.Message);

            _logger.Log(_logger.GetExceptionMessage(ex), Category.Exception, Priority.High);
        }
        #endregion

        #region Private and Protected Methods 
        /// <summary>
        /// Get ClearData Dialog Options.
        /// </summary>
        /// <returns></returns>
        private DialogOptions GetClearDataDialogOptions()
        {
            DialogOptions options = new DialogOptions();
            options.AllowResize = false;
            options.DialogTitle = Properties.Resources.ClearAllDataCaption;
            options.DialogMessage = string.Format("{0}" + Environment.NewLine + Environment.NewLine + "{1}", 
                Properties.Resources.ClearAllDataMessage, Properties.Resources.ContinueClearAllDataMessage);
            options.IconUri = new Uri(MenuConstants.MESSAGE_ERROR_IMAGE_URI);
            options.Buttons = ButtonType.YesNo;

            return options;
        }

        /// <summary>
        /// Save.
        /// </summary>
        private void Save()
        {            
            _persistenceService.Save();
            Model.SaveCommand.RaiseCanExecuteChanged();
        }
        #endregion

        #region Public and internal Methods
        #endregion

        #region Event Handlers
        #endregion

        #region Base Class Overrides
        #endregion
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Australia Australia
I've been working as a software developer since 2000 and hold a Bachelor of Business degree from The Open Polytechnic of New Zealand. Computers are for people and I aim to build applications for people that they would want to use.

Comments and Discussions