Click here to Skip to main content
Click here to Skip to main content

A Sample Silverlight 4 Application Using MEF, MVVM, and WCF RIA Services - Part 2

By , 8 Jul 2011
 
  • Download source files and setup package from Part 1

Article Series

This article is part two of a series on developing a Silverlight business application using MEF, MVVM Light, and WCF RIA Services.

Contents

Introduction

In this second part, we will go through various topics on how the MVVM Light Toolkit is used in our sample application. I chose this toolkit mainly because it is lightweight. Also, it is one of the most popular MVVM frameworks supporting Silverlight 4.

RelayCommand

One of the new features in Silverlight 4 is a pair of properties added to the ButtonBase class named Command and CommandParameter. This commanding infrastructure makes MVVM implementations a lot easier in Silverlight. Let's take a look at how RelayCommand is used for the "Delete User" button from the User Maintenance screen. First, we define the XAML code of the button as follows:

<Button Grid.Row="2" Grid.Column="0"
        VerticalAlignment="Top" HorizontalAlignment="Right" 
        Width="75" Height="23" Margin="0,5,167,5" 
        Content="Delete User"
        Command="{Binding Path=RemoveUserCommand}"
        CommandParameter="{Binding SelectedItem, ElementName=comboBox_UserName, 
                          ValidatesOnNotifyDataErrors=False}"/>

The code above specifies that we should call the RemoveUserCommand defined in UserMaintenanceViewModel.cs and pass in a parameter of the currently selected user when the "Delete User" button is clicked. And, the RelayCommand RemoveUserCommand is defined as:

private RelayCommand<User> _removeUserCommand = null;

public RelayCommand<User> RemoveUserCommand
{
    get
    {
        if (_removeUserCommand == null)
        {
            _removeUserCommand = new RelayCommand<User>(
                OnRemoveUserCommand,
                g => (issueVisionModel != null) && 
                       !(issueVisionModel.HasChanges) && (g != null));
        }
        return _removeUserCommand;
    }
}

private void OnRemoveUserCommand(User g)
{
    try
    {
        if (!_issueVisionModel.IsBusy)
        {
            // cancel any changes before deleting a user
            if (_issueVisionModel.HasChanges)
            {
                _issueVisionModel.RejectChanges();
            }

            // ask to confirm deleting the current user
            var dialogMessage = new DialogMessage(
                this,
                Resources.DeleteCurrentUserMessageBoxText,
                s =>
                {
                    if (s == MessageBoxResult.OK)
                    {
                        // if confirmed, removing CurrentUser
                        _issueVisionModel.RemoveUser(g);

                        // cache the current user name as empty string
                        _userNameToDisplay = string.Empty;

                        _operation = UserMaintenanceOperation.Delete;
                        IsUpdateUser = true;
                        IsAddUser = false;

                        _issueVisionModel.SaveChangesAsync();
                    }
                })
            {
                Button = MessageBoxButton.OKCancel,
                Caption = Resources.ConfirmMessageBoxCaption
            };

            AppMessages.PleaseConfirmMessage.Send(dialogMessage);
        }
    }
    catch (Exception ex)
    {
        // notify user if there is any error
        AppMessages.RaiseErrorMessage.Send(ex);
    }
}

The code snippet above, when called, will first display a message asking to confirm whether to delete the selected user or not. If confirmed, the functions RemoveUser() and SaveChangesAsync(), both defined in the IssueVisionModel class, will get called, thus removing the selected user from the database.

The second parameter of the RelayCommand is the CanExecute method. In the sample code above, it is defined as "g => (_issueVisionModel != null) && !(_issueVisionModel.HasChanges) && (g != null)", which means that the "Delete User" button is only enabled when there are no pending changes and the selected user is not null. Unlike WPF, this CanExecute method is not automatically polled in Silverlight when the HasChanges property changes, and we need to call the RaiseCanExecuteChanged method manually, like the following:

private void _issueVisionModel_PropertyChanged(object sender, 
             PropertyChangedEventArgs e)
{
    if (e.PropertyName.Equals("HasChanges"))
    {
        AddUserCommand.RaiseCanExecuteChanged();
        RemoveUserCommand.RaiseCanExecuteChanged();
        SubmitChangeCommand.RaiseCanExecuteChanged();
        CancelChangeCommand.RaiseCanExecuteChanged();
    }
}

Messenger

The Messenger class from MVVM Light Toolkit uses a simple Publish/Subscribe model to allow loosely coupled messaging. This facilitates communication between the different ViewModel classes as well as communication from the ViewModel class to the View class. In our sample, we define a static class called AppMessages that encapsulates all the messages used in this application.

/// <summary>
/// class that defines all messages used in this application
/// </summary>
public static class AppMessages
{
    ......

    public static class ChangeScreenMessage
    {
        public static void Send(string screenName)
        {
            Messenger.Default.Send(screenName, MessageTypes.ChangeScreen);
        }

        public static void Register(object recipient, Action<string> action)
        {
            Messenger.Default.Register(recipient, 
                      MessageTypes.ChangeScreen, action);
        }
    }

    public static class RaiseErrorMessage
    {
        public static void Send(Exception ex)
        {
            Messenger.Default.Send(ex, MessageTypes.RaiseError);
        }

        public static void Register(object recipient, Action<Exception> action)
        {
            Messenger.Default.Register(recipient, 
                      MessageTypes.RaiseError, action);
        }
    }

    public static class PleaseConfirmMessage
    {
        public static void Send(DialogMessage dialogMessage)
        {
            Messenger.Default.Send(dialogMessage, 
                      MessageTypes.PleaseConfirm);
        }

        public static void Register(object recipient, Action<DialogMessage> action)
        {
            Messenger.Default.Register(recipient, 
                      MessageTypes.PleaseConfirm, action);
        }
    }

    public static class StatusUpdateMessage
    {
        public static void Send(DialogMessage dialogMessage)
        {
            Messenger.Default.Send(dialogMessage, 
                      MessageTypes.StatusUpdate);
        }

        public static void Register(object recipient, Action<DialogMessage> action)
        {
            Messenger.Default.Register(recipient, 
                      MessageTypes.StatusUpdate, action);
        }
    }

    ......
}

In the code-behind file MainPage.xaml.cs, four AppMessages are registered. ChangeScreenMessage is registered to handle requests from the menu for switching between different screens. The other three AppMessages are all system-wide messages:

  • RaiseErrorMessage will display an error message if something goes wrong, and immediately logs off from the database.
  • PleaseConfirmMessage is used to display a message asking for user confirmation, and processes the call back based on user feedback.
  • StatusUpdateMessage is used to update the user on certain status changes, like a new issue has been successfully created and saved, etc.

Here is how we register the StatusUpdateMessage:

public MainPage()
{
    InitializeComponent();

    // register for StatusUpdateMessage
    AppMessages.StatusUpdateMessage.Register(this, OnStatusUpdateMessage);
    
    ......
}

#region "StatusUpdateMessage"

private static void OnStatusUpdateMessage(DialogMessage dialogMessage)
{
    if (dialogMessage != null)
    {
        MessageBoxResult result = MessageBox.Show(dialogMessage.Content,
            dialogMessage.Caption, dialogMessage.Button);

        dialogMessage.ProcessCallback(result);
    }
}

#endregion "StatusUpdateMessage"

And, here is how we can send a message to the StatusUpdateMessage:

......
// notify user of the new issue ID
var dialogMessage = new DialogMessage(
         this,
         Resources.NewIssueCreatedText + addedIssue.IssueID,
         null)
{
    Button = MessageBoxButton.OK,
    Caption = Resources.NewIssueCreatedCaption
};

AppMessages.StatusUpdateMessage.Send(dialogMessage);
......

EventToCommand

EventToCommand is a Blend behavior that is added as a new feature in the MVVM Light Toolkit V3, and is used to bind an event to an ICommand directly in XAML, which gives us the power to handle pretty much any event with RelayCommand from the ViewModel class.

Following is an example of how drag and drop of files is implemented in the "New Issue" screen. Let's check the XAML code first:

<ListBox x:Name="listBox_Files" Grid.Row="1" Grid.Column="0"
         AllowDrop="True"
         ItemsSource="{Binding Path=CurrentIssue.Files, ValidatesOnNotifyDataErrors=False}">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="{Binding Path=FileName, ValidatesOnNotifyDataErrors=False}" />
                <TextBlock Text="{Binding Path=Data.Length, StringFormat=' - \{0:F0\} bytes',
                                  ValidatesOnNotifyDataErrors=False}" />
            </StackPanel>
        </DataTemplate>
    </ListBox.ItemTemplate>
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="Drop">
            <cmd:EventToCommand PassEventArgsToCommand="True"
                    Command="{Binding Path=HandleDropCommand, Mode=OneWay}" />
        </i:EventTrigger>
    </i:Interaction.Triggers>
</ListBox>

The code above basically specifies that the ListBox allows drag-and-drop, and when a Drop event fires, the HandleDropCommand from the IssueEditorViewModel class gets called. Next, let's look at how HandleDropCommand is implemented:

private RelayCommand<DragEventArgs> _handleDropCommand = null;

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 (MemoryStream 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);
    }
}

HandleDropCommand will loop through the list of files dropped by the user, reads the content of each file, and then adds them into the Files EntityCollection. The data will later be saved to the database when the user saves the changes.

ICleanup Interface

Whenever the user chooses a different screen from the menu, a ChangeScreenMessage is sent, which eventually calls the following OnChangeScreenMessage method:

private void OnChangeScreenMessage(string changeScreen)
{
    // call Cleanup() on the current screen before switching
    var currentScreen = mainPageContent.Content as ICleanup;
    if (currentScreen != null)
        currentScreen.Cleanup();

    // reset noErrorMessage
    _noErrorMessage = true;

    switch (changeScreen)
    {
        case ViewTypes.HomeView:
            mainPageContent.Content = new Home();
            break;
        case ViewTypes.NewIssueView:
            mainPageContent.Content = new NewIssue();
            break;
        case ViewTypes.AllIssuesView:
            mainPageContent.Content = new AllIssues();
            break;
        case ViewTypes.MyIssuesView:
            mainPageContent.Content = new MyIssues();
            break;
        case ViewTypes.BugReportView:
            mainPageContent.Content = new Reports();
            break;
        case ViewTypes.MyProfileView:
            mainPageContent.Content = new MyProfile();
            break;
        case ViewTypes.UserMaintenanceView:
            mainPageContent.Content = new UserMaintenance();
            break;
        default:
            throw new NotImplementedException();
    }
}

From the code above, we can see that every time we switch to a new screen, the current screen is first being tested to see whether it supports the ICleanup interface. If it is, the Cleanup() method is called before switching to the new screen. In fact, any screen, except the Home screen which does not bind to any ViewModel class, implements the ICleanup interface.

The Cleanup() method defined in any of the View classes will first call the Cleanup() method on its ViewModel class to unregister any event handlers and AppMessages. Next, it will unregister any AppMessages used by the View class itself, and the last step is to release the ViewModel class by calling ReleaseExport<ViewModelBase>(_viewModelExport), thus making sure that there are no memory leaks. Let's look at an example:

public partial class Reports : UserControl, ICleanup
{
    #region "Private Data Members"
    private const double MinimumWidth = 640;
    private Lazy<ViewModelBase> _viewModelExport;
    #endregion "Private Data Members"

    #region "Constructor"
    public Reports()
    {
        InitializeComponent();
        // initialize the UserControl Width & Height
        Content_Resized(this, null);

        // register for GetChartsMessage
        AppMessages.GetChartsMessage.Register(this, OnGetChartsMessage);

        if (!ViewModelBase.IsInDesignModeStatic)
        {
            // Use MEF To load the View Model
            _viewModelExport = App.Container.GetExport<ViewModelBase>(
                ViewModelTypes.BugReportViewModel);
            if (_viewModelExport != null) DataContext = _viewModelExport.Value;
        }
    }
    #endregion "Constructor"

    #region "ICleanup interface implementation"

    public void Cleanup()
    {
        // call Cleanup on its ViewModel
        ((ICleanup)DataContext).Cleanup();
        // cleanup itself
        Messenger.Default.Unregister(this);
        // set DataContext to null and call ReleaseExport()
        DataContext = null;
        App.Container.ReleaseExport(_viewModelExport);
        _viewModelExport = null;
    }

    #endregion "ICleanup interface implementation"

    ......
}

And here is the Cleanup() method in its ViewModel class:

#region "ICleanup interface implementation"
public override void Cleanup()
{
    if (_issueVisionModel != null)
    {
        // unregister all events
        _issueVisionModel.GetAllUnresolvedIssuesComplete -= 
             _issueVisionModel_GetAllUnresolvedIssuesComplete;
        _issueVisionModel.GetActiveBugCountByMonthComplete -= 
             _issueVisionModel_GetActiveBugCountByMonthComplete;
        _issueVisionModel.GetResolvedBugCountByMonthComplete -= 
             _issueVisionModel_GetResolvedBugCountByMonthComplete;
        _issueVisionModel.GetActiveBugCountByPriorityComplete -= 
             _issueVisionModel_GetActiveBugCountByPriorityComplete;
        _issueVisionModel.PropertyChanged -= 
             _issueVisionModel_PropertyChanged;
        _issueVisionModel = null;
    }
    // set properties back to null
    AllIssues = null;
    ActiveBugCountByMonth = null;
    ResolvedBugCountByMonth = null;
    ActiveBugCountByPriority = null;
    // unregister any messages for this ViewModel
    base.Cleanup();
}
#endregion "ICleanup interface implementation"

Next Steps

In this article, we visited the topics of how the MVVM Light toolkit is used: namely, RelayCommand, Messenger, EventToCommand, and ICleanup. In our last part, we will focus on how custom authentication, reset password, and user maintenance are done through WCF RIA Services.

I hope you find this article useful, and please rate and/or leave feedback below. Thank you!

History

  • May 2010 - Initial release
  • July 2010 - Minor update based on feedback
  • November 2010 - Update to support VS2010 Express Edition
  • February 2011 - Update to fix multiple bugs including memory leak issues
  • July 2011 - Update to fix multiple bugs

License

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

About the Author

Weidong Shen
Software Developer (Senior)
United States United States
Member
Weidong has been an information system professional since 1990. He has a Master's degree in Computer Science, and is currently a MCSD .NET

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionWhat a great articlememberessdataylor30 Aug '12 - 8:57 
I didn't even read all of the content and already am excited to see what can be done. Thanks for sharing.
AnswerRe: What a great articlememberWeidong Shen30 Aug '12 - 14:21 
Thanks! Smile | :)
GeneralMy vote of 4memberEdgars Palacis5 Jul '12 - 9:59 
Good explanation of MVVMLight usage. There are lackage of MVVMLight documents. This one gives brief insight.
GeneralThis article is a Fraud (please read on)...memberremesqny3 Jan '11 - 10:33 
I am going to go out on a limb here, so if I insult anyone because I am WRONG, then I apologize in advance. However, if I am anywhere near correct I am hopeful that the CodeProject.com God's figure out a way to correct the situation (if they deem it necessary to do so).
 
First, I invested A LOT of time going over the 4 articles associated with this Issue Vision, supposed issue tracker "example". I am no sleuth, but I was nonetheless fooled by these articles (as I will point out). I am also no expert, and so I rely on many articles on codeproject.com to learn. When I saw this "example" I was excited to have a framework to build upon for my own personal projects. So whatever you might say in response, yes, it's my own fault for falling for this fraud.
 
Second, this article is exactly that, a fraud. I was having trouble creating a new view in this project. No matter what I did (even what Mr. Shen suggested in his response - before he suggested it), I kept getting an error. I scanned around the messages to each of the 4 articles to see if a similar question had been asked. It had not been asked (surprisingly, leading me to believe I was truly out of my realm for what seemed like a basic task). But, and this was my first indication of the fraud, I noticed that Mr. Shen's answers to many specific "how to" questions were vague and common sense responses without any indiciation of what someone should do for their particular request. So, I posted the question of how to add views. Mr. Shen's response was so vague as to cause me to spend 1/2 hour deciding whether or not I should continue investing time with the "example". I had already invested over 20 hours reading, learning and trying to build off the example.
 
Initially, I just thought that Mr. Shen was a busy man and probably took the approach of "teacher" by trying to point me in the right direction without giving me the answer expecting me to learn it (the "teach a man to fish" scenario). I could have respected that. But then I found this: MS Line of Business Sample.
 
That, for those of you who are unaware of the fraud, is Microsoft's very own Line of Business Sample for Silverlight 4. It is exactly Issue Vision as Mr. Shen presents it. If you scroll down to the sample's "Home" page rendition, you will not it has the same language as in Issue Vision. Note that in article 1 on this, Mr. Shen states:
 
"This three part article series is my effort at creating such a sample. The choice of an issue tracking application comes from David Poll's PDC09 talk, and the design architecture is from Shawn Wildermuth's blog posts."
 
I did not find anything about this architecture from Mr. Wildermuth's posts (other than just his posts about an architecture such as that used in Issue Vision by Mr. Shen). However, I must admit that after that 1/2 hour of deliberating I abandoned the idea of trying to learn from Issue Vision because I could not figure out the errors I was getting when trying to add a new view.
 
So, IMHO, Mr. Shen failed to answer my question only because i suspect he does not know the answer. All he did was dress up Issue Vision from the MS example and called it his own.
 
I am not so naive as to believe this has never happened on codeproject.com or anywhere else on the web. However, given the response from Mr. Shen, and given my INADVERDENT discovery of the MS example, and given the time I invested with Mr. Shen's "example", I felt it warranted my post to warn others and hopefully to get codeproject.com to act. I have been using this site for over 7 years to help out with learning this stuff, and it's a shame.
 
Why do I care, you might ask? Because you know that Mr. Shen is pimping this article out there in the world claiming to have created something he did not. Plagarism, Mr. Shen, is not teaching.
 
I will be cross-posting this for maximum exposure. If Mr. Shen is able to delete comments, then the integrity of codeproject.com is seriously compromised.
GeneralRe: This article is a Fraud (please read on)... [modified]memberJulien Villers5 Aug '11 - 5:01 
Have you even looked at both projects' source codes?
 
Microsoft doesn't use MVVM light for instance, project structure is completely different, etc...
To me, this requires some heavy work, even if the original source code might have been taken from an official sample.
 
It's OK to be frustrated and to yell against yourself, it's another to start calling names and accusing people on shaky grounds.
 

EDIT: so, univote here? Nice Dead | X|
'As programmers go, I'm fairly social. Which still means I'm a borderline sociopath by normal standards.' Jeff Atwood
modified on Wednesday, September 21, 2011 4:11 AM

QuestionHow do you add new views (User Control)memberremesqny30 Dec '10 - 14:25 
First, thanks for this sample as it is the most complete I've found to help me understand and build upon for my own work. I am new to Silverlight, having spent some time with ASP.net and recently getting into MVC. I realized Silverlight's RIA is what I've wanted all along, because I am trying to build a simple business application.
 
I have been trying for several hours to add a new "view" to the Menu. At first, I tried to add a simple user control. I tried following the "Home" example, so as to try it out and minimize the potential for messing up. This meant, I surmised, just adding the view to ViewTypes.cs and not having any code in the SilverlightControl1.xaml and SilverlightControl1View.xaml.cs file I created. I also added the MenuItem to MainPage.xaml and the ViewType to MainPage.xaml.cs (called it SilverlightControlView with "new SilverlightControl1();". I also added two "case" statements for SilverlightControl1View to the MainPageViewModel.cs file. None of that worked. I got the "the method or operation is not implemented" error box which leads to the login screen.
 
Along with the above, I then tried to add a ViewModel, SilverlightCntrol1ViewModel to ViewModelTypes.cs. I also attempted to construct SilverlightControlViewModel.cs using the other ...ViewModel.cs files. This is where I believe I messed up, because again, I got the "method or operation is not implemented" error box. I tried to figure out which syntax in the Private Data Members region, Constructor region, Public Properties region, Public Commands region, ICleanup Interface region and Private Methods region I would need to use in the SilverlightControl1.xaml.cs file. Again, this was a basic user control just to test adding new views, so I didn't believe I needed some of the syntax.
 
The full error message is: The method or operation is not implemented. Error Details: at IssueVision.ViewModel.MainPageViewModel.OnChangeScreenCommand(String g) which is in the MainPageViewModel.cs file.
 
The SilverlightControl1.xaml file has the following code, which I copied from the other .xaml files:
 
="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:IssueVision.Client;assembly=IssueVision.Client"
xmlns:toolkit="http://schemas.microsoft.com/winfx/2006/xaml/presentation/toolkit"
 
Loaded="UserControl_Loaded" Unloaded="UserControl_Unloaded"
mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480">
 

 


 
I did read Part 2 through in order to try and figure out how to add a view, but have been unsuccessful. As I mentioned, I have been trying to figure it out for a few hours now. I don't want to give up.
 
I appreciate any help. Also, as a future update request (if you plan on any), maybe you're response can be implemented as a paragraph to explain how to add a new view.
 
Thank you, again.
 
-REM
AnswerRe: How do you add new views (User Control)memberWeidong Shen30 Dec '10 - 17:57 
I have the impression that you are trying to get too many things done with one shot.
 
With MVVM, you should be able to add a View without thinking about the ViewModel or Model. I would suggest you to comment out the code related to the DataContext when you are adding a new View, and check whether you can get that new View showing without any error message. Put some break points if you get any error.
 
Have fun!
AnswerRe: How do you add new views (User Control)memberDrammy5 Jan '11 - 3:44 
Hi REM,
 
The NotImplementedException is thrown in the default case for the switch statements in the OnChangeScreen command - although you probably know that already.
 
The reason why is because none of the earlier case statements match true. They don't match true because perhaps you haven't added a CONST string for your new view to the IssueVision.Common.ViewTypes class or perhaps there is a typo between the CONST value and the CommandParameter value you have specified in your new menu item in MainPage.xaml. If you look at the OnChangeScreenCommand method you'll see there is a string (g) parameter in it's signature - that is the value passed via the CommandParameter defined in the menu item.
 
To create a new view, lets call it "About"
 
1. Add a menu item in one of the MainPage.xaml menus; in either the LoggedInMenuContent or the LoggedOutMenuContent. Give it whatever name you like, the command must be bound to the ChangeScreenCommand and the CommandParameter can be "About".
2. Add a CONST string to the IssueVision.Common.ViewTypes, where the constant value matches the CommandParameter defined in 1.
public const string AboutView = "About"
3. Add a case statement to the OnChangeScreenCommand method (don't forget there are 2 switch statements - as you pointed out in your post).
case ViewTypes.AboutView
// open the About screen
AppMessages.ChangeScreenMessage.Send(ViewTypes.AboutView);
CurrentScreenText = ViewTypes.AboutView;
break;
4. Create a ViewModel called AboutViewModel and ensure it inherets from ViewModelBase, has the Export(ViewModelTypes.AboutViewModel) and PartCreationPolicy(CreationPolicy.NonShared) MEF attributes, and has a ImportingConstructor attributed Constructor and ICleanup implementation (like the existing ViewModels). Don't worry about any properties, methods or commands just yet.
5. Add a CONST string to the IssueVision.Common.ViewModelTypes, where the value matches the name of the new ViewModel.
public const string AboutViewModel = "AboutViewModel";
6. Create the About screen view (AboutView.xaml) as a UserControl, don't bother with anything else for now.
7. In the codebehind for AboutView.xaml ensure it inherits from ICleanup, has a standard MEF SatisfyImports in the constructor and has a ViewModel (object) property with the Import(ViewModelTypes.AboutViewModel) attribute which sets the DataContext. Ensure it also implements the ICleanup interface and then leave any other methods for now.
 
You should now have a working View that can be called from the menu, if so, now you can start to play with changing the look and feel by adding content, properties, commands and methods to the View and ViewModel.
 
Hope that helps, let me know if I missed something out...
GeneralRe: How do you add new views (User Control)memberMember 767071323 Mar '11 - 21:07 
Hi.
First of all, thanks for the useful steps.
However I wasn't able to see the newly added view.
I didn't quite get the step 7 as the 'has a standard MEF SatisfyImports in the constructor and has a ViewModel (object) property with the Import(ViewModelTypes.AboutViewModel) attribute which sets the DataContext.' part is absent from all other code-behind files.
Am I missing something?
Any advice would be greatly appreciated
GeneralRe: How do you add new views (User Control)memberWeidong Shen24 Mar '11 - 3:48 
good question. I was using the standard MEF SatisfyImports in the constructor and had a ViewModel (object) property with the Import to set the DataContext before. But someone reported that this sample causes memory leaks, and I found that one of the causes of memory leaks is using this SatisfyImports and Import approach.
 
The old approach is fine as long as you are not creating a new view and disposing the old view every time you switch screens. But if you do, here is the new approach:
 
1) In the constructor, I set the DataContext with the following lines:
 
if (!ViewModelBase.IsInDesignModeStatic)
{
    // Use MEF To load the View Model
    _viewModelExport = App.Container.GetExport<ViewModelBase>(
        ViewModelTypes.BugReportViewModel);
    this.DataContext = _viewModelExport.Value;
}
 
2) In the Cleanup() function, I release the ViewModel with the following lines:
 
// set DataContext to null and call ReleaseExport()
this.DataContext = null;
App.Container.ReleaseExport<ViewModelBase>(_viewModelExport);
_viewModelExport = null;
 
This second step will notify MEF to release the ViewModel and avoid memory leaks.
 
Hope this helps. Thanks!
 
Weidong Shen

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web03 | 2.6.130523.1 | Last Updated 8 Jul 2011
Article Copyright 2010 by Weidong Shen
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid