Click here to Skip to main content
15,886,110 members
Articles / Desktop Programming / XAML

A Pluggable Architecture for Building Silverlight Applications with MVVM

Rate me:
Please Sign up or sign in to vote.
4.71/5 (23 votes)
6 Jul 2011CPOL7 min read 144.9K   2.2K   90  
This article describes building a sample Silverlight application with the MVVM Light toolkit, WCF RIA Services, and a pluggable application architecture using MEF.
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.IO;
using System.Linq;
using System.Windows;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using GalaSoft.MvvmLight.Messaging;
using IssueVision.Data.Web;
using IssueVision.Common;
using MVVMPlugin;

namespace IssueVision.ViewModel
{
    [ExportPlugin(ViewModelTypes.IssueEditorViewModel, PluginType.ViewModel)]
    [PartCreationPolicy(CreationPolicy.NonShared)]
    public class IssueEditorViewModel : ViewModelBase
    {
        #region "Private Data Members"
        private IIssueVisionModel _issueVisionModel;
        private Issue _currentIssueCache;
        #endregion "Private Data Members"

        #region "Constructor"
        public IssueEditorViewModel()
        {
            _issueVisionModel = PluginCatalogService.Container.GetExportedValue<IIssueVisionModel>();

            // set up event handling
            _issueVisionModel.GetIssueTypesComplete += _issueVisionModel_GetIssueTypesComplete;
            _issueVisionModel.GetPlatformsComplete += _issueVisionModel_GetPlatformsComplete;
            _issueVisionModel.GetResolutionsComplete += _issueVisionModel_GetResolutionsComplete;
            _issueVisionModel.GetStatusesComplete += _issueVisionModel_GetStatusesComplete;
            _issueVisionModel.GetSubStatusesComplete += _issueVisionModel_GetSubStatusesComplete;
            _issueVisionModel.GetUsersComplete += _issueVisionModel_GetUsersComplete;

            // set _currentIssueCache to null
            _currentIssueCache = null;

            // load issue type entries
            IssueTypeEntries = null;
            _issueVisionModel.GetIssueTypesAsync();
            // load platform entries
            PlatformEntries = null;
            _issueVisionModel.GetPlatformsAsync();
            //load resolution entries
            ResolutionEntriesWithNull = null;
            _issueVisionModel.GetResolutionsAsync();
            // load status entries
            StatusEntries = null;
            _issueVisionModel.GetStatusesAsync();
            // load substatus entries
            SubstatusEntriesWithNull = null;
            _issueVisionModel.GetSubStatusesAsync();
            // load user entries
            UserEntries = null;
            UserEntriesWithNull = null;
            _issueVisionModel.GetUsersAsync();

            // register for EditIssueMessage
            AppMessages.EditIssueMessage.Register(this, OnEditIssueMessage);
        }
        #endregion "Constructor"

        #region "ICleanup interface implementation"
        public override void Cleanup()
        {
            if (_issueVisionModel != null)
            {
                // unregister all events
                _issueVisionModel.GetIssueTypesComplete -= _issueVisionModel_GetIssueTypesComplete;
                _issueVisionModel.GetPlatformsComplete -= _issueVisionModel_GetPlatformsComplete;
                _issueVisionModel.GetResolutionsComplete -= _issueVisionModel_GetResolutionsComplete;
                _issueVisionModel.GetStatusesComplete -= _issueVisionModel_GetStatusesComplete;
                _issueVisionModel.GetSubStatusesComplete -= _issueVisionModel_GetSubStatusesComplete;
                _issueVisionModel.GetUsersComplete -= _issueVisionModel_GetUsersComplete;
                _issueVisionModel = null;
            }
            // set properties back to null
            CurrentIssue = null;
            IssueTypeEntries = null;
            PlatformEntries = null;
            ResolutionEntriesWithNull = null;
            StatusEntries = null;
            SubstatusEntriesWithNull = null;
            UserEntries = null;
            UserEntriesWithNull = null;
            // unregister any messages for this ViewModel
            base.Cleanup();
        }
        #endregion "ICleanup interface implementation"

        #region "Public Properties"

        private Issue _currentIssue;

        public Issue CurrentIssue
        {
            get { return _currentIssue; }
            private set
            {
                if (!ReferenceEquals(_currentIssue, value))
                {
                    _currentIssue = value;
                    RaisePropertyChanged("CurrentIssue");
                }
            }
        }

        private IEnumerable<byte> _priorityEntries;

        public IEnumerable<byte> PriorityEntries
        {
            get
            {
                if (_priorityEntries == null)
                {
                    var priorityList = new List<byte>();
                    for (var i = IssueVisionServiceConstant.HighestPriority; i <= IssueVisionServiceConstant.LowestPriority; i++)
                    {
                        priorityList.Add(i);
                    }
                    _priorityEntries = priorityList.AsEnumerable();
                }
                return _priorityEntries;
            }
        }

        private IEnumerable<byte> _severityEntries;

        public IEnumerable<byte> SeverityEntries
        {
            get
            {
                if (_severityEntries == null)
                {
                    var severityList = new List<byte>();
                    for (var i = IssueVisionServiceConstant.HighestSeverity; i <= IssueVisionServiceConstant.LowestSeverity; i++)
                    {
                        severityList.Add(i);
                    }
                    _severityEntries = severityList.AsEnumerable();
                }
                return _severityEntries;
            }
        }

        private IEnumerable<IssueType> _issueTypeEntries;

        public IEnumerable<IssueType> IssueTypeEntries
        {
            get { return _issueTypeEntries; }
            private set
            {
                if (!ReferenceEquals(_issueTypeEntries, value))
                {
                    _issueTypeEntries = value;
                    RaisePropertyChanged("IssueTypeEntries");
                }
            }
        }

        private IEnumerable<Platform> _platformEntries;

        public IEnumerable<Platform> PlatformEntries
        {
            get { return _platformEntries; }
            private set
            {
                if (!ReferenceEquals(_platformEntries, value))
                {
                    _platformEntries = value;
                    RaisePropertyChanged("PlatformEntries");
                }
            }
        }

        private IEnumerable<Resolution> _resolutionEntriesWithNull;

        public IEnumerable<Resolution> ResolutionEntriesWithNull
        {
            get { return _resolutionEntriesWithNull; }
            private set
            {
                if (!ReferenceEquals(_resolutionEntriesWithNull, value))
                {
                    _resolutionEntriesWithNull = value;
                    RaisePropertyChanged("ResolutionEntriesWithNull");
                }
            }
        }

        private IEnumerable<Status> _statusEntries;

        public IEnumerable<Status> StatusEntries
        {
            get { return _statusEntries; }
            private set
            {
                if (!ReferenceEquals(_statusEntries, value))
                {
                    _statusEntries = value;
                    RaisePropertyChanged("StatusEntries");
                }
            }
        }

        private IEnumerable<SubStatus> _substatusEntriesWithNull;

        public IEnumerable<SubStatus> SubstatusEntriesWithNull
        {
            get { return _substatusEntriesWithNull; }
            private set
            {
                if (!ReferenceEquals(_substatusEntriesWithNull, value))
                {
                    _substatusEntriesWithNull = value;
                    RaisePropertyChanged("SubstatusEntriesWithNull");
                }
            }
        }

        private IEnumerable<User> _userEntries;

        public IEnumerable<User> UserEntries
        {
            get { return _userEntries; }
            private set
            {
                if (!ReferenceEquals(_userEntries, value))
                {
                    _userEntries = value;
                    RaisePropertyChanged("UserEntries");
                }
            }
        }

        private IEnumerable<User> _userEntriesWithNull;

        public IEnumerable<User> UserEntriesWithNull
        {
            get { return _userEntriesWithNull; }
            private set
            {
                if (!ReferenceEquals(_userEntriesWithNull, value))
                {
                    _userEntriesWithNull = value;
                    RaisePropertyChanged("UserEntriesWithNull");
                }
            }
        }

        #endregion "Public Properties"

        #region "Public Commands"

        private RelayCommand<DragEventArgs> _handleDropCommand;

        public RelayCommand<DragEventArgs> HandleDropCommand
        {
            get
            {
                if (_handleDropCommand == null)
                {
                    _handleDropCommand = new RelayCommand<DragEventArgs>(
                        OnHandleDropCommand,
                        e => CurrentIssue != null);
                }
                return _handleDropCommand;
            }
        }

        private void OnHandleDropCommand(DragEventArgs e)
        {
            try
            {
                // get a list of files as FileInfo objects
                var files = e.Data.GetData(DataFormats.FileDrop) as FileInfo[];

                if (files != null)
                {
                    // loop through the list and read each file
                    foreach (var file in files)
                    {
                        using (var fs = file.OpenRead())
                        using (var ms = new MemoryStream())
                        {
                            fs.CopyTo(ms);
                            // and then add each file into the Files entity collection
                            CurrentIssue.Files.Add(
                                new Data.Web.File
                                {
                                    FileID = Guid.NewGuid(),
                                    IssueID = CurrentIssue.IssueID,
                                    FileName = file.Name,
                                    Data = ms.GetBuffer()
                                });
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                // notify user if there is any error
                AppMessages.RaiseErrorMessage.Send(ex);
            }
        }

        private RelayCommand _addFileCommand;

        public RelayCommand AddFileCommand
        {
            get
            {
                if (_addFileCommand == null)
                {
                    _addFileCommand = new RelayCommand(
                        OnAddFileCommand,
                        () => CurrentIssue != null);
                }
                return _addFileCommand;
            }
        }

        private void OnAddFileCommand()
        {
            try
            {
                if (!_issueVisionModel.IsBusy)
                {
                    FileInfo addedFile = null;
                    // ask user for a file to add
                    AppMessages.OpenFileMessage.Send(
                        new NotificationMessageAction<FileInfo>("Open File", g => addedFile = g));

                    if (addedFile != null)
                    {
                        using (var fs = addedFile.OpenRead())
                        using (var ms = new MemoryStream())
                        {
                            fs.CopyTo(ms);
                            // and then add the file into the Files entity collection
                            CurrentIssue.Files.Add(
                                new Data.Web.File
                                {
                                    FileID = Guid.NewGuid(),
                                    IssueID = CurrentIssue.IssueID,
                                    FileName = addedFile.Name,
                                    Data = ms.GetBuffer()
                                });
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                // notify user if there is any error
                AppMessages.RaiseErrorMessage.Send(ex);
            }
        }

        private RelayCommand<Data.Web.File> _removeFileCommand;

        public RelayCommand<Data.Web.File> RemoveFileCommand
        {
            get
            {
                if (_removeFileCommand == null)
                {
                    _removeFileCommand = new RelayCommand<Data.Web.File>(
                        OnRemoveFileCommand,
                        g => g != null);
                }
                return _removeFileCommand;
            }
        }

        private void OnRemoveFileCommand(Data.Web.File g)
        {
            try
            {
                if (!_issueVisionModel.IsBusy)
                {
                    CurrentIssue.Files.Remove(g);
                    _issueVisionModel.RemoveFile(g);
                }
            }
            catch (Exception ex)
            {
                // notify user if there is any error
                AppMessages.RaiseErrorMessage.Send(ex);
            }
        }

        private RelayCommand<Data.Web.File> _saveToDiskCommand;

        public RelayCommand<Data.Web.File> SaveToDiskCommand
        {
            get
            {
                if (_saveToDiskCommand == null)
                {
                    _saveToDiskCommand = new RelayCommand<Data.Web.File>(g =>
                    {
                        if (!_issueVisionModel.IsBusy)
                        {
                            AppMessages.SaveFileMessage.Send(g);
                        }
                    }, g => g != null);
                }
                return _saveToDiskCommand;
            }
        }

        private RelayCommand _addAttributeCommand;

        public RelayCommand AddAttributeCommand
        {
            get
            {
                if (_addAttributeCommand == null)
                {
                    _addAttributeCommand = new RelayCommand(
                        OnAddAttributeCommand,
                        () => CurrentIssue != null);
                }
                return _addAttributeCommand;
            }
        }

        private void OnAddAttributeCommand()
        {
            if (!_issueVisionModel.IsBusy)
            {
                CurrentIssue.Attributes.Add(
                    new Data.Web.Attribute
                    {
                        ID = Guid.NewGuid(),
                        IssueID = CurrentIssue.IssueID,
                        AttributeName = "Key",
                        Value = "Value"
                    });
            }
        }

        private RelayCommand<Data.Web.Attribute> _removeAttributeCommand;

        public RelayCommand<Data.Web.Attribute> RemoveAttributeCommand
        {
            get
            {
                if (_removeAttributeCommand == null)
                {
                    _removeAttributeCommand = new RelayCommand<Data.Web.Attribute>(g =>
                    {
                        if (!_issueVisionModel.IsBusy)
                        {
                            CurrentIssue.Attributes.Remove(g);
                            _issueVisionModel.RemoveAttribute(g);
                        }
                    }, g => g != null);
                }
                return _removeAttributeCommand;
            }
        }

        #endregion "Public Commands"

        #region "Private Methods"

        private void _issueVisionModel_GetIssueTypesComplete(object sender, EntityResultsArgs<IssueType> e)
        {
            if (!e.HasError)
            {
                IssueTypeEntries = e.Results.OrderBy(g => g.Name);
                // check whether IssueTypeEntries is populated after CurrentIssue
                AssignCurrentIssue(null);
            }
            else
            {
                // notify user if there is any error
                AppMessages.RaiseErrorMessage.Send(e.Error);
            }
        }

        private void _issueVisionModel_GetPlatformsComplete(object sender, EntityResultsArgs<Platform> e)
        {
            if (!e.HasError)
            {
                PlatformEntries = e.Results.OrderBy(g => g.OS).ThenBy(g => g.OSVersion);
                // check whether PlatformEntries is populated after CurrentIssue
                AssignCurrentIssue(null);
            }
            else
            {
                // notify user if there is any error
                AppMessages.RaiseErrorMessage.Send(e.Error);
            }
        }

        private void _issueVisionModel_GetResolutionsComplete(object sender, EntityResultsArgs<Resolution> e)
        {
            if (!e.HasError)
            {
                // create a new resolution list with the first item of null Name
                var resolutionList = e.Results.ToList();
                resolutionList.Add(new Resolution());
                ResolutionEntriesWithNull = resolutionList.OrderBy(g => g.Name).AsEnumerable();
                // check whether ResolutionEntriesWithNull is populated after CurrentIssue
                AssignCurrentIssue(null);
            }
            else
            {
                // notify user if there is any error
                AppMessages.RaiseErrorMessage.Send(e.Error);
            }
        }

        private void _issueVisionModel_GetStatusesComplete(object sender, EntityResultsArgs<Status> e)
        {
            if (!e.HasError)
            {
                StatusEntries = e.Results.OrderBy(g => g.StatusID);
                // check whether StatusEntries is populated after CurrentIssue
                AssignCurrentIssue(null);
            }
            else
            {
                // notify user if there is any error
                AppMessages.RaiseErrorMessage.Send(e.Error);
            }
        }

        private void _issueVisionModel_GetSubStatusesComplete(object sender, EntityResultsArgs<SubStatus> e)
        {
            if (!e.HasError)
            {
                // create a new substatus list with the first item of null Name
                var substatusList = e.Results.ToList();
                substatusList.Add(new SubStatus());
                SubstatusEntriesWithNull = substatusList.OrderBy(g => g.Name).AsEnumerable();
                // check whether SubstatusEntriesWithNull is populated after CurrentIssue
                AssignCurrentIssue(null);
            }
            else
            {
                // notify user if there is any error
                AppMessages.RaiseErrorMessage.Send(e.Error);
            }
        }

        private void _issueVisionModel_GetUsersComplete(object sender, EntityResultsArgs<User> e)
        {
            if (!e.HasError)
            {
                UserEntries = e.Results.OrderBy(g => g.Name);
                // create a new user list with the first item of null user name
                var userList = e.Results.ToList();
                userList.Add(new User());
                UserEntriesWithNull = userList.OrderBy(g => g.Name).AsEnumerable();
                // check whether UserEntries/UserEntriesWithNull is populated after CurrentIssue
                AssignCurrentIssue(null);
            }
            else
            {
                // notify user if there is any error
                AppMessages.RaiseErrorMessage.Send(e.Error);
            }
        }

        private void OnEditIssueMessage(Issue editIssue)
        {
            if (editIssue == null) return;
            // check whether this issue is read-only or not
            AppMessages.ReadOnlyIssueMessage.Send(editIssue.IsIssueReadOnly());

            AssignCurrentIssue(editIssue);
        }

        /// <summary>
        /// Assign edit issue only after all ComboBox are ready
        /// </summary>
        /// <param name="editIssue"></param>
        private void AssignCurrentIssue(Issue editIssue)
        {
            if (editIssue != null)
            {
                // this call is coming from OnEditIssueMessage()
                if (IssueTypeEntries != null && PlatformEntries != null &&
                    ResolutionEntriesWithNull != null && StatusEntries != null &&
                    SubstatusEntriesWithNull != null && UserEntries != null &&
                    UserEntriesWithNull != null)
                {
                    // if all ComboBox are ready, we set CurrentIssue
                    CurrentIssue = editIssue;
                    _currentIssueCache = null;
                }
                else
                    _currentIssueCache = editIssue;
            }
            else
            {
                // this call is coming from one of the complete event handlers
                if (_currentIssueCache != null && IssueTypeEntries != null &&
                    PlatformEntries != null && ResolutionEntriesWithNull != null &&
                    StatusEntries != null && SubstatusEntriesWithNull != null &&
                    UserEntries != null && UserEntriesWithNull != null)
                {
                    // if all ComboBox are ready, we set CurrentIssue
                    CurrentIssue = _currentIssueCache;
                    _currentIssueCache = null;
                }
            }
        }

        #endregion "Private Methods"
    }
}

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)
United States United States
Weidong has been an information system professional since 1990. He has a Master's degree in Computer Science, and is currently a MCSD .NET

Comments and Discussions