Click here to Skip to main content
Click here to Skip to main content
 
Add your own
alternative version

Migrate from Basic to MVVM and MEF Composable Patterns for a Silverlight Application - Part 3

, 3 May 2012 CPOL
The article series shows how to upgrade a Silverlight application having basic patterns to the MVVM and MEF composable patterns with easy approaches and detailed coding explanations.
MigrateToMvvmMef-Part1_Src.zip
MigrateToMvvmMef-Part1_Src
ProductApp.Common
Bin
Release
Constants
ModuleServices
obj
ProductApp.Common.csproj.user
Properties
ProductApp.Main
Assets
Bin
Release
obj
ProductApp.Main.csproj.user
Properties
Views
ProductApp.suo
ProductApp.Views
Assets
Bin
Release
obj
ProductApp.Views.csproj.user
Properties
ProductApp.Web
bin
ClientBin
obj
ProductApp.Web.csproj.user
Properties
ProductRiaLib
ProductRiaLib.Web
App_Data
bin
Debug
EntityFramework.dll
Microsoft.ServiceModel.DomainServices.EntityFramework.dll
Release
Models
obj
ProductRiaLib.Web.csproj.user
Properties
Services
Bin
Release
obj
ProductRiaLib.csproj.user
Properties
MigrateToMvvmMef-Part2_Src.zip
MigrateToMvvmMef-Part2_Src
ProductApp.Common
Bin
Release
Constants
EventArguments
Models
ModuleServices
obj
ProductApp.Common.csproj.user
Properties
ProductApp.Main
Assets
Bin
Release
obj
ProductApp.Main.csproj.user
Properties
ViewModels
Views
ProductApp.Models
Bin
Release
obj
ProductApp.Models.csproj.user
Properties
ProductApp.suo
ProductApp.ViewModels
Bin
Release
obj
ProductApp.ViewModels.csproj.user
Properties
ProductApp.Views
Assets
Bin
Release
obj
ProductApp.Views.csproj.user
Properties
ProductApp.Web
bin
ClientBin
obj
ProductApp.Web.csproj.user
Properties
ProductRiaLib
ProductRiaLib.Web
App_Data
bin
Debug
EntityFramework.dll
Microsoft.ServiceModel.DomainServices.EntityFramework.dll
Release
Models
obj
ProductRiaLib.Web.csproj.user
Properties
Services
Bin
Release
obj
ProductRiaLib.csproj.user
Properties
_Assemblies
GalaSoft.MvvmLight.SL5.dll
System.Windows.Interactivity.dll
MigrateToMvvmMef-Part3_Src.zip
MigrateToMvvmMef-Part3_Src
AnotherXap.Models
AnotherXap.Models.csproj.user
Bin
Release
obj
Properties
AnotherXap.ViewModels
AnotherXap.ViewModels.csproj.user
Bin
Release
obj
Properties
AnotherXap.Views
AnotherXap.Views.csproj.user
Assets
Bin
Release
obj
Properties
ProductApp.Common
Bin
Release
Constants
EventArguments
Models
ModuleServices
obj
ProductApp.Common.csproj.user
Properties
SaveState
ProductApp.Main
Assets
Bin
Release
Models
obj
ProductApp.Main.csproj.user
Properties
ViewModels
Views
ProductApp.Models
Bin
Release
obj
ProductApp.Models.csproj.user
Properties
ProductApp.suo
ProductApp.ViewModels
Bin
Release
obj
ProductApp.ViewModels.csproj.user
Properties
ProductApp.Views
Assets
Bin
Release
obj
ProductApp.Views.csproj.user
Properties
ProductApp.Web
bin
ClientBin
obj
ProductApp.Web.csproj.user
Properties
ProductRiaLib
ProductRiaLib.Web
App_Data
bin
Debug
EntityFramework.dll
Microsoft.ServiceModel.DomainServices.EntityFramework.dll
Release
Models
obj
ProductRiaLib.Web.csproj.user
Properties
Services
Bin
Release
obj
ProductRiaLib.csproj.user
Properties
_Assemblies
GalaSoft.MvvmLight.SL5.dll
System.Windows.Interactivity.dll
using System;
using System.Windows;
using System.Linq;
using System.ComponentModel;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Primitives;
using System.Collections.ObjectModel;
using ProductRiaLib.Web.Services;
using ProductRiaLib.Web.Models;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using GalaSoft.MvvmLight.Messaging;
using ProductApp.Common;


namespace ProductApp.ViewModels
{
    [Export(typeof(IModule)), ExportMetadata(MetadataKeys.Name, ModuleID.ProductListViewModel)]
    [PartCreationPolicy(CreationPolicy.NonShared)]
    public class ProductListViewModel : ViewModelBase, IModule
    {        
        private IProductListModel _productListModel;
        private Category _comboDefault = new Category { CategoryID = -1, CategoryName = "Please Select" };
        
        public ProductListViewModel()
        {
            // Inject the Model as the Lazy object with the defined type
            _productListModel = ModuleCatalogService.Container.GetExport<IProductListModel>(ModuleID.ProductListModel).Value;
           
            // Register the event handlers defined in the Model            
            _productListModel.GetCategoryLookupComplete += ProductListModel_GetCategryComplete;
            _productListModel.GetCategorizedProductsComplete += ProductListModel_GetCategorizedProductComplete;
            _productListModel.SaveChangesComplete += ProductListModel_SaveChangesComplete;

            // Register message handler
            Messenger.Default.Register(this, MessageToken.DataToProductListVmMessage, new Action<Product>(OnDataToProductListVmMessage));

            // Send callback message to retrieve the state info
            Messenger.Default.Send(new NotificationMessageAction<StateCacheList<StateCache>>
                              (ModuleID.ProductListViewModel, OnGetStateMessageCallback),
                               MessageToken.GetStateMessage);

            // Get data for Category dropdown list
            _productListModel.GetCategoryLookup();
        }

        private void OnGetStateMessageCallback(StateCacheList<StateCache> stateList)
        {
            if (stateList != null)
            {
                // Re-populate the SelectedCategory prorperty
                SelectedCategory = (from w in stateList
                                    where w.MemberName == "SelectedCategory"
                                    select w.MemberValue).FirstOrDefault() as Category;
            }
        }

        private void SaveStateForMe()
        {
            // Send message for saving state            
            var stateList = new StateCacheList<StateCache>{
                       new StateCache { ModuleID = ModuleID.ProductListViewModel, 
                                         MemberName = "SelectedCategory",
                                         MemberValue = SelectedCategory,
                                       } 
                       // Other StateCache item can be added here...
            };
            Messenger.Default.Send(stateList, MessageToken.PutStateMessage);
        }    

        private ObservableCollection<Category> _categoryItems;
        //
        public ObservableCollection<Category> CategoryItems
        {
            get { return _categoryItems; }
            private set
            {
                if (!ReferenceEquals(_categoryItems, value))
                {
                    _categoryItems = value;
                    RaisePropertyChanged("CategoryItems");
                }
            }
        }

        private Category _selectedCategory;
        //
        public Category SelectedCategory
        {
            get { return _selectedCategory; }
            set
            {
                if (!ReferenceEquals(_selectedCategory, value))
                {
                    _selectedCategory = value;
                    RaisePropertyChanged("SelectedCategory");
                }
            }
        }
        
        private ObservableCollection<Product> _productItems;
        //
        public ObservableCollection<Product> ProductItems
        {
            get { return _productItems; }
            private set
            {
                if (!ReferenceEquals(_productItems, value))
                {
                    _productItems = value;
                    RaisePropertyChanged("ProductItems");
                }
            }
        }

        private Product _currentProduct;
        //-->
        public Product CurrentProduct
        {
            get { return _currentProduct; }
            set
            {
                if (!ReferenceEquals(_currentProduct, value))
                {
                    _currentProduct = value;
                    RaisePropertyChanged("CurrentProduct");
                }
            }
        }

        private RelayCommand _categorySelectionChanged;
        //
        public RelayCommand CategorySelectionChanged
        {
            get
            {
                if (_categorySelectionChanged == null)
                {
                    _categorySelectionChanged = new RelayCommand(
                        OnCategorySelectionChanged,
                        () => true);
                }
                return _categorySelectionChanged;
            }
        }
        //
        private void OnCategorySelectionChanged()
        {            
            try
            {
                // Reload the data to context based on the selected category
                Category item = SelectedCategory;
                if (item != null && item.CategoryID > 0)
                {
                    _productListModel.GetCategorizedProducts((int)item.CategoryID);                    
                }

                // Remove the "Please Select"
                if (SelectedCategory != _comboDefault)
                {
                    CategoryItems.Remove(_comboDefault);
                }

                // Enable the Add button
                AddProductCommand.RaiseCanExecuteChanged();

            }
            catch (Exception ex)
            {
                Messenger.Default.Send(ex, MessageToken.RaiseErrorMessage);
            }
        }

        private RelayCommand _addProductCommand;
        //
        public RelayCommand AddProductCommand
        {
            get
            {
                if (_addProductCommand == null)
                {
                    _addProductCommand = new RelayCommand(
                        OnAddProductCommand,
                        () => SelectedCategory != null && SelectedCategory.CategoryID > 0);
                }
                return _addProductCommand;
            }
        }
        //
        private void OnAddProductCommand()
        {
            Messenger.Default.Send("", MessageToken.LoadAddProductViewMessage);
            Messenger.Default.Send(SelectedCategory, MessageToken.DataToAddProductVmMessage);
        }
        //
        private void OnDataToProductListVmMessage(Product addedProduct)
        {
            try
            {
                if (!_productListModel.IsBusy && addedProduct != null)
                {
                    // Add to Context in the model
                    _productListModel.AddNewProduct(addedProduct);

                    // Add to ObCollection for refreshing display
                    ProductItems.Add(addedProduct);
                }
            }
            catch (Exception ex)
            {
                Messenger.Default.Send(ex, MessageToken.RaiseErrorMessage);
            }
        }

        private RelayCommand _saveChangesCommand;
        //
        public RelayCommand SaveChangesCommand
        {
            get
            {
                if (_saveChangesCommand == null)
                {
                    _saveChangesCommand = new RelayCommand(
                        OnSaveChangesCommand,
                        () => CurrentProduct != null);
                }
                return _saveChangesCommand;
            }
        }
        //
        private void OnSaveChangesCommand()
        {
            try
            {
                if (!_productListModel.IsBusy && _productListModel.HasChanges)
                {
                    _productListModel.SaveChanges(OperationTypes.Update);
                    SaveChangesCommand.CanExecute(false);
                }                
            }
            catch (Exception ex)
            {
                Messenger.Default.Send(ex, MessageToken.RaiseErrorMessage);
            }
        }
        
        private RelayCommand _deleteProductCommand;
        //
        public RelayCommand DeleteProductCommand
        {
            get
            {
                if (_deleteProductCommand == null)
                {
                    _deleteProductCommand = new RelayCommand(
                        OnDeleteProductCommand,
                        () => CurrentProduct != null);
                }
                return _deleteProductCommand;
            }
        }
        //
        private void OnDeleteProductCommand()
        {
            try
            {
                if (!_productListModel.IsBusy && CurrentProduct != null)
                {
                    // Send dialog message with callback from user response
                    MessageBoxResult theResult = MessageBoxResult.Cancel;
                    var dialogMessage = new DialogMessage(this,
                            StaticText.MessageBoxDeleteConfirmation,
                            rs => theResult = rs)
                    {
                        Button = MessageBoxButton.OKCancel,
                        Caption = StaticText.MessageBoxConfirmationCaption
                    };
                    
                    Messenger.Default.Send(dialogMessage, MessageToken.UseDialogMessage);
                    
                    if (theResult == MessageBoxResult.OK)
                    {
                        _productListModel.DeleteProduct(CurrentProduct);
                        
                        // Remove the item in the display
                        ProductItems.Remove(CurrentProduct);
                    }
                }
            }
            catch (Exception ex)
            {
                Messenger.Default.Send(ex, MessageToken.RaiseErrorMessage);
            }
        }

        private RelayCommand _dataGridSelectionChanged;
        //
        public RelayCommand DataGridSelectionChanged
        {
            get
            {
                if (_dataGridSelectionChanged == null)
                {
                    _dataGridSelectionChanged = new RelayCommand(
                        OnDataGridSelectionChanged,
                        () => true);
                }
                return _dataGridSelectionChanged;
            }
        }
        //
        private void OnDataGridSelectionChanged()
        {
            try
            {
                // Enable buttons                
                SaveChangesCommand.RaiseCanExecuteChanged();
                DeleteProductCommand.RaiseCanExecuteChanged();
            }
            catch (Exception ex)
            {
                Messenger.Default.Send(ex, MessageToken.RaiseErrorMessage);
            }
        }
          
        private void ProductListModel_GetCategryComplete(object sender, QueryResultsArgs<Category> e)
        {
            if (!e.HasError)
            {
                // Set the returned data from async loading to data object
                CategoryItems = e.Results;                
                
                if (SelectedCategory == null)
                {
                    // Add the combo default item "Please Select"
                    CategoryItems.Insert(0, _comboDefault);

                    // Set selected category if not from state cache
                    SelectedCategory = CategoryItems[0];
                }
            }
            else
            {
                // Send error message
                Messenger.Default.Send(e.Error, MessageToken.RaiseErrorMessage);
            }
        }
        
        private void ProductListModel_GetCategorizedProductComplete(object sender, QueryResultsArgs<Product> e)
        {
            if (!e.HasError)
            {                
                ProductItems = e.Results;
            }
            else
            {
                // Send error message
                Messenger.Default.Send(e.Error, MessageToken.RaiseErrorMessage);
            }
        }

        private void ProductListModel_SaveChangesComplete(object sender, SubmitOperationArgs e)
        {
            string msgBody = string.Empty;
            if (!e.HasError)
            {                                
                // Send different messages for particular type of operations
                switch (_productListModel.CurrentOperation)
                {                    
                    case OperationTypes.AddNew:
                        msgBody = StaticText.MessageBoxAddNewDone;
                        break;
                    case OperationTypes.Update:
                        msgBody = StaticText.MessageBoxSaveChangesDone;
                        break;
                    case OperationTypes.Delete:
                        msgBody = StaticText.MessageBoxDeleteDone;
                        break;
                 default:
                        msgBody = StaticText.MessageBoxSaveChangesDone;
                        break;
                }

                var dialogMessage = new DialogMessage(this, msgBody, null)
                {
                    Button = MessageBoxButton.OK,
                    Caption = StaticText.MessageBoxStatusCaption
                };

                Messenger.Default.Send(dialogMessage, MessageToken.UseDialogMessage);
            }
            else
            {
                // Send error message
                Messenger.Default.Send(e.Error, MessageToken.RaiseErrorMessage);
            }
        }

        public override void Cleanup()
        {
            // Call for saving state info
            SaveStateForMe();

            if (_productListModel != null)
            {
                // Unregister all event handling                
                _productListModel.GetCategoryLookupComplete -= ProductListModel_GetCategryComplete;
                _productListModel.GetCategorizedProductsComplete -= ProductListModel_GetCategorizedProductComplete;
                _productListModel.SaveChangesComplete -= ProductListModel_SaveChangesComplete;

                // No clean-up for shared Model module, just set instance to null                
                _productListModel = null;
            }

            // Set data catch for properties to null            
            _categoryItems = null;
            _productItems = null;
            _selectedCategory = null;
            _currentProduct = null;
            _comboDefault = null;

            // Unregister any message for this ViewModel
            base.Cleanup();
        }
    }
}

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)

Share

About the Author

Shenwei Liu

United States United States
Shenwei is a software developer and architect, and has been working on business applications using Microsoft and Oracle technologies since 1996. He obtained Microsoft Certified Systems Engineer (MCSE) in 1998 and Microsoft Certified Solution Developer (MCSD) in 1999. He has experience in ASP.NET, C#, Visual Basic, Windows and Web Services, Silverlight, WPF, JavaScript/AJAX, HTML, SQL Server, and Oracle.

| Advertise | Privacy | Mobile
Web01 | 2.8.141022.1 | Last Updated 3 May 2012
Article Copyright 2012 by Shenwei Liu
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid