(X) Hide this
    • Login
    • Join
      • Generate New Image
        By clicking 'Register' you accept the terms of use .

ModalDialogs, IEditableObject and MVVM in Silverlight 4

(26 votes)
Pencho Popadiyn
>
Pencho Popadiyn
Joined Apr 30, 2008
Articles:   22
Comments:   97
More Articles
49 comments   /   posted on May 11, 2010
Tags:   ieditableobject , modal-dialogs , memento , mvvm , pencho-popadiyn

This article is compatible with the latest version of Silverlight.


1. Introduction

I recently found myself in the following situation. My boss came to me and asked me: “Dude, we have a big Silverlight project, performing a great number of CRUD operations. We need to find a unified way of showing Modal Dialogs in a MVVM manner, without using any code in the View. The logic of showing dialogs must reside in the ViewModel, not in the code-behind. Also do you remember our previous WinForms projects? There are several pretty useful classes, such as BindingSource and DataRowView, implementing BeginEdit, EndEdit and CancelEdit functionality out of the box. We need to bring this functionality in our Silverlight projects, however, this time on business objects level. It must be ready till tonight, otherwise you won’t get salary at the end of the month!!!

Knowing my boss, the last sentence must have been a joke, or at least I hope to be a joke :) After working a few hours on the problem, I found a solution, which makes me feel pretty happy. That’s why I decided to share it with you guys.

2. Modal Dialogs and MVVM

So here is the general idea. First, we need to find a way to show modal dialogs. Fortunately, Silverlight 3 and Silverlight 4 provide us with ChildWindow. Of course, with the same success you can use dialogs (windows) provided by any third-party component vendor. However, the most important question is how to use a Modal Dialog inside the ViewModel and in the same time to remain decoupled. One of the most important OOP principles – The Dependency Inversion Principle teaches us to depend on abstractions not on concrete implementations. When we have static dependencies in our code, we can break them by using either an interface or a base class. Additionally, according to the MVVM pattern, the ViewModel doesn’t have to know about the View. Definitely we need an interface. Here it is:

So we need a DialogResult to determine whether the OK or Cancel button is clicked. We need to have the possibility to Close and Show the dialog; to be notified when the dialog is closed, in order to perform the subsequent actions. And finally, let’s take a look at the Content property. It is a little bit more special. Why? The answer is that there are two possible solutions of the current problem. In the first one, we can exchange the Content property with DataContext property. Or in other words, we need to create a new modal dialog for each entity. After that we just set the target entity to the dialog’s DataContext, like on the image below.

The second solution requires some additional work. Instead of having a separate child window for each entity, you could use UserControls. After that, you just set the UserControl to the dialog’s Content property.

Personally I prefer the second one, and namely it is demonstrated in this demo. However, the first one is also “playable”. Here is the IModalView interface. It is a quite simple, and provides a way to set the DataContext property of the UserControl, as well as to be notified when OK or Cancel button is pressed.

Finally, we need to bring in another level of abstraction on the scene. The final piece is the glue between the ModalDialog, the ModalView, and the ViewModel. This is the IModalDialogWorker interface. It provides only one method for showing dialogs and completely hiding the details of the implementation.

As you can see, when you show modal dialogs, you have to pass implementations of IModalDialog and IModalView interfaces, a dataContext (this is the currently edited entity – e.g. Person, Customer, Employee, etc). Finally, you need to pass an action that is invoked when the dialog is closed. The last parameter is optional (e.g. you can use that method to show custom MessageBox dialogs). Via the IModalDialog’s DialogResult property, you can obtain the dialog result.

The ModalDialogWorker implementation is quite simple. It is just a matter of coding, here it is:

public class ModalDialogWorker : IModalDialogWorker
{
    public void ShowDialog<T>( IModalDialog modalDialog, IModalView modalView, T dataContext, Action<T> onClosed )
    {
        if ( modalDialog == null )
            throw new ArgumentNullException( "modalDialog", "Cannot be null" );
        if ( modalView == null )
            throw new ArgumentNullException( "modalView", "Cannot be null" );
 
        EventHandler onDialogClosedHandler = null;
        EventHandler<ModalViewEventArgs> onViewClosedHandler = null;
 
        if ( onClosed != null )
        {
            onDialogClosedHandler = ( s, a ) =>
            {
                modalDialog.Closed -= onDialogClosedHandler;
                onClosed( dataContext );
            };
 
            onViewClosedHandler = ( s, a ) =>
            {
                modalDialog.Closed -= onDialogClosedHandler;
                modalView.Closed -= onViewClosedHandler;
                modalDialog.DialogResult = a.DialogResult;
                //modalDialog.Close();
                onClosed( dataContext );
            };
 
            modalDialog.Closed += onDialogClosedHandler;
            modalView.Closed += onViewClosedHandler;
        }
 
        modalDialog.Content = modalView;
        modalView.DataContext = dataContext;
        modalDialog.ShowDialog();
    }
}

Now let’s see how to create IModalDialog and IModalView implementations. The ModalDialog is very simple. You just have to derive from ChildWindow and implement the IModalDialog interface.

public class ExtendedChildWindow : ChildWindow, IModalDialog
{
    public void ShowDialog()
    {
        this.Show();
    }
}

Note that the ExtendedChildWindow dialog is universal, you just have to set its content property with the concrete XXXModalView. For example, if you want to create a control for editing Person objects, you need to create a new UserControl and implement IModalView interface.

public partial class EditPersonControl : UserControl, IModalView
{
    public EditPersonControl()
    {
        InitializeComponent();
    }
 
    public event EventHandler<ModalViewEventArgs> Closed;
 
    protected virtual void OnClosed( ModalViewEventArgs e )
    {
        if ( this.Closed != null )
            this.Closed( this, e );
    }
 
    private void btnOkClick( object sender, RoutedEventArgs e )
    {
        this.OnClosed( new ModalViewEventArgs( true ) );
    }
 
    private void btnCancelClick( object sender, RoutedEventArgs e )
    {
        this.OnClosed( new ModalViewEventArgs( false ) );
    }
}

The only things you should do in the code-behind is to mark the control as IModalView implementation; to set the DialogResult to true, if user clicked OK, and to false if user clicked Cancel. Note that this is done in the code-behind because it is not part of the business logic, but of the UI logic.

The key moments in the ModalDialogWorker are these three lines of code:

modalDialog.Content = modalView;
modalView.DataContext = dataContext;
modalDialog.ShowDialog();

3. “M” for Mighty

The question remains how to inject the IModalDialog, IModalVIew and IModalDialogWorker implementations into the ViewModel.

Fortunately, we have MEF in our tool belt. You need to mark all composable parts with the Export attribute and to specify contracts.

On the other side of the channel, the ViewModel, you have to specify the Imports. After that, using the ModalDialogWorker is just a piece of cake.

private void OnEditPersonCommandExecute()
{
    this.ModalDialogWorker.ShowDialog<Person>(
        this.ModalDialog, this.EditPersonControl, this.SelectedPerson, p =>
        {
            if ( this.ModalDialog.DialogResult.HasValue &&
                this.ModalDialog.DialogResult.Value )
            {
                // OK
            }
            else
            {
                // Cancel
            }
        } );
}

Of course you could use any other discovering patterns.

4. Generic Implementation of IEditableObject

The next important bunch of questions is: What happens when the user edits objects? What should happen when he/she presses the OK or Cancel button? How to commit the changes? How to restore the original state of the object?

We need to commit or rollback changes through the BeginEdit, EndEdit and CancelEdit methods. That is easy. We can use IEditableObject interface. The most important problem is how to keep a snapshot of the object’s state. To address this bullet, we can use the Memento pattern. The function of the Memento pattern is to capture and externalize an object’s internal state so that the object can be restored to this state later, without violating encapsulation.

Well having this in mind, let’s create a new generic class named Memento<T>.

public class Memento<T>
{
    private Dictionary<PropertyInfo, object> storedProperties = new Dictionary<PropertyInfo, object>();
 
    public Memento( T originator )
    {
        this.InitializeMemento( originator );
    }
 
    public T Originator
    {
        get;
        protected set;
    }
 
    public void Restore( T originator )
    {
        foreach ( var pair in this.storedProperties )
        {
            pair.Key.SetValue( originator, pair.Value, null );
        }
    }
 
    private void InitializeMemento( T originator )
    {
        if ( originator == null )
            throw new ArgumentNullException( "Originator", "Originator cannot be null" );
 
        this.Originator = originator;
        IEnumerable<PropertyInfo> propertyInfos = typeof( T ).GetProperties( BindingFlags.Public | BindingFlags.Instance )
                                        .Where( p => p.CanRead && p.CanWrite );
 
        foreach ( PropertyInfo property in propertyInfos )
            this.storedProperties[ property ] = property.GetValue( originator, null );
    }
}

The code is quite simple. First you are passing the originator (this is the object we want to track). In the InitializeMemento method, you are taking the internal state. All properties and their values are stored in a simple Dictionary<string,object>.

this.Originator = originator;
IEnumerable<PropertyInfo> propertyInfos = typeof( T ).GetProperties( 
                                BindingFlags.Public | BindingFlags.Instance )
                                .Where( p => p.CanRead && p.CanWrite );
 
foreach ( PropertyInfo property in propertyInfos )
    this.storedProperties[ property ] = property.GetValue( originator, null );

Finally, in the RestoreState method, we are taking the original values and restoring the object’s initial state.

public void Restore( T originator )
{
    foreach ( var pair in this.storedProperties )
    {
        pair.Key.SetValue( originator, pair.Value, null );
    }
}

The last piece of the puzzle is the Caretaker. The Caretaker is a simple wrapper. It implements the IEditableObject interface and has a reference to the generic Memento<T>. This is the place where the magic happens. However, the code is even simpler than the code for Memento<T>.

public class Caretaker<T> : IEditableObject
{
    private Memento<T> memento;
    private T target;
 
    public T Target
    {
        get
        {
            return this.target;
        }
        protected set
        {
            if ( value == null )
            {
                throw new ArgumentNullException( "Target", "Target cannot be null" );
            }
 
            if ( Object.ReferenceEquals( this.Target, value ) )
                return;
 
            this.target = value;
        }
    }
 
    public Caretaker( T target )
    {
        this.Target = target;
    }
 
    public void BeginEdit()
    {
        if ( this.memento == null )
            this.memento = new Memento<T>( this.Target );
    }
 
    public void CancelEdit()
    {
        if ( this.memento == null )
            throw new ArgumentNullException( "Memento", "BeginEdit() is not invoked" );
 
        this.memento.Restore( Target );
        this.memento = null;
    }
 
    public void EndEdit()
    {
        if ( this.memento == null )
            throw new ArgumentNullException( "Memento", "BeginEdit() is not invoked" );
 
        this.memento = null;
    }
}

Now, you are free to use the Caretaker in the following manner:

Caretaker<Person> editableObject = new Caretaker<Person>( this.SelectedPerson );
editableObject.BeginEdit();
//.....
this.SelectedPerson.Name = "Pesho";
// Commit Changes
editableObject.EndEdit();
// -or CancelChanges
// editableObject.CancelEdit();

Let’s go back to our PersonViewModel, and update the OnEditPersonCommandExecute method:

private void OnEditPersonCommandExecute()
{
    Caretaker<Person> editableObject = new Caretaker<Person>( this.SelectedPerson );
    editableObject.BeginEdit();
 
    this.ModalDialogWorker.ShowDialog<Person>(
        this.ModalDialog, this.EditPersonControl, this.SelectedPerson, p =>
        {
            if ( this.ModalDialog.DialogResult.HasValue &&
                this.ModalDialog.DialogResult.Value )
            {
                editableObject.EndEdit();
            }
            else
            {
                editableObject.CancelEdit();
            }
        } );
}

5. Final Words

You can download the source code from here.

The other goodies: Note that the whole business logic is located in the ViewModel. No actions in the code-behind. Also pay attention to the fact that at each point the ViewModel deals with interfaces, no concrete implementations. This is important, because it is not recommended to have a reference in the ViewModel to ChildWindow (for example). I am a big fan of MEF, that’s why I am using it wherever this is possible. However, marking the concrete implementations with the Export attribute doesn’t obligate you to use MEF for parts discovering. You are free to use any other pattern (approach). Finally, using DelegateCommand – this is standard, no need of explanation.

And the online demo. Select a person from the datagrid. Press the Edit button, a new modal dialog appears. This is happening thanks to the three interfaces presented in the article. Make some changes to the current Person object. Click either the OK or Cancel button. The changes are committed, or respectively rejected. This is happening thanks to the generic implementations of the IEditableObject and the Memento pattern.

So, that’s it. I know the problem for performing CRUD operations in Silverlight/WPF is not so simple and I hope that the code here will be useful for you. Feel free to experiment with it. If you have any idea for further improvements or questions, I will be happy if you share it with us.


Subscribe

Comments

  • -_-

    RE: ModalDialogs, IEditableObject and MVVM in Silverlight 4


    posted by WPFDeveloper on May 11, 2010 14:04
    Very nice article.  Great work.
  • -_-

    RE: ModalDialogs, IEditableObject and MVVM in Silverlight 4


    posted by s. singh on May 11, 2010 18:46
    Very nice. I've just started creating a bunch of SL4 ModalDialogs for a project..your article was timely.
  • -_-

    RE: ModalDialogs, IEditableObject and MVVM in Silverlight 4


    posted by Balaji on May 12, 2010 07:57
    Nice Article !!!!!
  • -_-

    RE: ModalDialogs, IEditableObject and MVVM in Silverlight 4


    posted by simple on May 12, 2010 17:08
    This is way too much code.  This is really over complicating things.  Keep it simple.
  • ppopadiyn

    RE: ModalDialogs, IEditableObject and MVVM in Silverlight 4


    posted by ppopadiyn on May 12, 2010 17:16

    Hi,

    If you mean the modal dialogs, i agree with you that using additional user controls makes the program more complex. Maybe using directly ChildWindows is better approach ;). Thanks for advice

  • -_-

    RE: ModalDialogs, IEditableObject and MVVM in Silverlight 4


    posted by mike kidder on May 18, 2010 03:23
    Pencho, thanks for sharing this elegant solution, source code, and well written article....
  • -_-

    RE: ModalDialogs, IEditableObject and MVVM in Silverlight 4


    posted by Andre Baltieri on May 21, 2010 05:09

    Amazing solution. A little complex to me yet, but will continue my studies on MVVM and MEF with SL.
    This will realy help me a lot!

    Thanks man!

  • -_-

    RE: ModalDialogs, IEditableObject and MVVM in Silverlight 4


    posted by aitran on May 21, 2010 10:22
    Im not using MEF for the whole solution, and I try to use IModalDialog, then it throws an exception of null in the ViewModel ModalDialogWorker
    Please help,
    Thanks.
  • ppopadiyn

    RE: ModalDialogs, IEditableObject and MVVM in Silverlight 4


    posted by ppopadiyn on May 21, 2010 10:27
    Hi aitran, probably, you need pass somehow to the ViewModel instances of the implementations (for example you can create a new constructor for the view model and pass instances of the ModalDialogWorker, ModalDialog, ModalView, etc). If you don't use MEF, then this is the nature of .Net, it will not create instances of these classes and when you try to use them NullReferenceException will be thrown. Let me know if this works for you.
  • -_-

    RE: ModalDialogs, IEditableObject and MVVM in Silverlight 4


    posted by Denis Vuyka on Jun 02, 2010 18:39

    This is a great solution and your samples help me a lot. However I've made some corrections to code:

    1. Some value comparison will work differently for reference and value types. So I've restricted Memento and Proxy with a constraint: "where T : class"

    2. Memento won't work properly for the class containing properties with non-public setters (at least for Silverlight 3). I've updated the Memento.InitializeMemento with additional check for public setter presence:

    foreach (var property in propertyInfos)
          {
            if (property.GetSetMethod() == null) continue;
            _storedProperties[property] = property.GetValue(originator, null);
          }

    Regards,

    Denis

  • ppopadiyn

    RE: ModalDialogs, IEditableObject and MVVM in Silverlight 4


    posted by ppopadiyn on Jun 02, 2010 18:43
    Hi Denis, I am very happy you find the samples useful and thanks a lot for the improvements ;)
  • -_-

    RE: ModalDialogs, IEditableObject and MVVM in Silverlight 4


    posted by Alex on Jun 05, 2010 14:52

    Very good article. Nice and clean solution.

    I have one question, what to do with collections (collection of objects|)?

  • -_-

    RE: ModalDialogs, IEditableObject and MVVM in Silverlight 4


    posted by Thanigainatahn on Jun 05, 2010 21:02

    Hi There,

    Very nice article.Good oppotunity to learn the Patterns.

    Thanks,

    Thani

  • Calabonga

    RE: ModalDialogs, IEditableObject and MVVM in Silverlight 4


    posted by Calabonga on Jul 19, 2010 03:49

    Very good article! Thanks for your shared code.

    But what about MVVM + RIA Services modal dialog and IEditableObject?

  • ppopadiyn

    RE: ModalDialogs, IEditableObject and MVVM in Silverlight 4


    posted by ppopadiyn on Jul 21, 2010 10:53

    Hello Calabonga,

    Fortunately, in WCF RIA all exposed business objects implements IEditableObject automatically. All changes are tracked in an EntityChangeSet object, and the changes are submitted collectively when you call SubmitChanges method of your domain context (respectively rejected, when you call the RejectChanges method). So you could use the same approach with the modal dialogs and mvvm. The only difference is that you should work directly with your domain context.

  • -_-

    RE: ModalDialogs, IEditableObject and MVVM in Silverlight 4


    posted by Richard on Jul 21, 2010 11:25

    Hi

    I've converted your code to vb.net but am getting issues. I'm not an expert on OOP/classes...this is 2 examples errors (about 19 similiar)

    Class 'PersonViewModel' must implement 'Sub OnImportsSatisfied()' for interface 'System.ComponentModel.Composition.IPartImportsSatisfiedNotification'.  ..\...\Demo\ViewModels\PersonViewModel.vb

    Class 'ExtendedChildWindow' must implement 'Sub Close()' for interface 'Framework.IModalDialog'. Views\ExtendedChildWindow.vb


    My understanding is that as the ExtendedChildWindow implements Framework.IModalDialog
     it should have a Close() method - the C# version does not - likewise the OnImportsSatisfied sub exists in PersonViewModel
     but does not have the keyword 'Implements IPartImportsSatisfiedNotification' attached to it (C# version has this missing as well but no compilation error). If I add Implements IPartImportsSatisfiedNotification to the OnImportsSatisfied
     sub I still get compilation error - I am now confused.  Anyone one of you guru's shed some light/advice on this (obviouslt some difference in conversion from C# to Vb.net??)


    Great article.

    Richard





  • ppopadiyn

    RE: ModalDialogs, IEditableObject and MVVM in Silverlight 4


    posted by ppopadiyn on Jul 21, 2010 11:37

    Hi Richard,

    Sorry but I am not an expert in VB.NET. As regards your question about the ExtendedChildWindow and IModalDialog. The ExtendedChildWindow class derives from ChildWindow and implements the IModalDialog interface. "it should have a Close() method - the C# version does not" - you are absolutely right there must be a Close method in the ExtendedChildWindow dialog, but it doesn't exists. However the base ChildWindow class has a Close method, or in other words the Close method is implemented in the base ChildWindow class, and the compiler obviously is pretty smart :) to recognize this. Just for a test you could rename the "Close" method in the C# code, for example to "Close2" and see the result, now you will have a compiler error.

  • -_-

    RE: ModalDialogs, IEditableObject and MVVM in Silverlight 4


    posted by Richard on Jul 21, 2010 13:15

    ppopadyyn

    Thanks for the prompt reply, okay I will look into further and hopefully resolve.

    If anyone else has converted this code to Vb.net  please get back to me.

    Thanks




  • -_-

    RE: ModalDialogs, IEditableObject and MVVM in Silverlight 4


    posted by dbis on Aug 02, 2010 00:21
    ppopadyyn, could you please clarify how you close modal dialog? Snippet
    modalDialog.Close() is commented out
  • ppopadiyn

    RE: ModalDialogs, IEditableObject and MVVM in Silverlight 4


    posted by ppopadiyn on Aug 02, 2010 08:01

    Hi dbis

    Setting the DialogResult property of the ChildWindow, automatically close it (that is the behavior of the ChildWindow). That happens in the ModalDialogWorker, line 40.

  • -_-

    RE: ModalDialogs, IEditableObject and MVVM in Silverlight 4


    posted by dbis on Aug 02, 2010 20:26
    Thanks!
  • Calabonga

    RE: ModalDialogs, IEditableObject and MVVM in Silverlight 4


    posted by Calabonga on Aug 04, 2010 03:20
    Thank for your comment! That's what I realy need.
  • -_-

    RE: ModalDialogs, IEditableObject and MVVM in Silverlight 4


    posted by Brian on Aug 04, 2010 16:39

    There is a bug in your sample.


    1. Select a Person.
    2. Click Edit.
    3. Modify the Name, and leave the focus in the Name text box.
    4. Click the X in the upper right of the dialog to close it.
    5. The Name was updated when it shouldn't have been.


  • -_-

    RE: ModalDialogs, IEditableObject and MVVM in Silverlight 4


    posted by stephan on Aug 26, 2010 15:17
    well done
  • -_-

    RE: ModalDialogs, IEditableObject and MVVM in Silverlight 4


    posted by jay on Sep 06, 2010 21:18

    Great post!  Your demo worked great. 

    I tried to use sections of it in my application. I keep getting the error message saying [Type of ......ModalDialog is not CLS_Compliant]. Any ideas ?


     [Import]

            public IModalDialog ModalDialog

            {

                get;

                set;

            }




  • -_-

    RE: ModalDialogs, IEditableObject and MVVM in Silverlight 4


    posted by Jay on Sep 13, 2010 06:21

    If I wanted to pass a string parameter to the DeleteControl in this situation, how would I go about it? I was thinking of passing a customized warning message to the control. I added the parameter in the constructor like this: 

     public DeleteControl (string myMessage)



  • -_-

    RE: ModalDialogs, IEditableObject and MVVM in Silverlight 4


    posted by ppopadiyn on Sep 13, 2010 09:54

    Hello Jay,

    I can think of three possible solutions

    • As you guessed, a parameter in the constructor of the control.
    • Add an additional property of type Hashtable (or generic Dictionary) in the IModalView interface.
    • If you want to use MEF, you could define "Property Imports" or  use "Importing Constructor". Find more info here.
  • -_-

    RE: ModalDialogs, IEditableObject and MVVM in Silverlight 4


    posted by J on Sep 13, 2010 18:35

    Thanks ppopadiyn! 

    Please ignore the duplicate post.

    Cheers!

     

  • -_-

    RE: ModalDialogs, IEditableObject and MVVM in Silverlight 4


    posted by J on Sep 14, 2010 03:49

    Ppopadyin, I still need help with getting  this example to work when sending a customized message to the dialog box using MEF. Can u give me some code. Looked at the link you provided but I'm still not getting it.

    Thanks

  • ppopadiyn

    RE: ModalDialogs, IEditableObject and MVVM in Silverlight 4


    posted by ppopadiyn on Sep 14, 2010 08:28

    Hello J,

    Here is a modified demo showing you how to pass custom delete message by using mef. Take a look at the following classes:

    • Code - behind for DeleteControl.xaml, I've added an additional property named "CustomDeleteMessage". See the [Import] attribute.
    • I've created an additional class named StringConfigurator - it is located in the Constants.cs file. There, the [Export] attribute is used. Note that, you should pass a contract name for both of the attributes. It must be the same in order to work.

    Another possible solution is to create an additional class named DeleteControlViewModel - this will be a view model for the delete control. And in the DeleteControl, you will bind the TextBox.Text property to the corresponding string property of the DeleteControlViewModel. See the PersonViewModel, in the OnDeletePersonCommandExecute method. Instead of passing this.SelectedPerson as a DataContext to the DeleteControl you will pass a new instance of the DeleteControlViewModel. Personally I prefer the second solution. But the first one is also playable.

    Please if you have any further questions do not hesitate to contact me.

  • -_-

    RE: ModalDialogs, IEditableObject and MVVM in Silverlight 4


    posted by J on Sep 15, 2010 02:56
    Hi again, Ppopadyin
    I used your code and it worked. I wanted to add further customization but due to the following line in my main control

      if (!GalaSoft.MvvmLight.ViewModelBase.IsInDesignModeStatic)

                {

                    // Use MEF To load the View Model                

                    CompositionInitializer.SatisfyImports(this);

                }


    the entire ViewModel is loaded. I placed a variant of your code (not in the Constants.cs, as suggested) but in my ViewModel like so:

     [Export("CustomDeleteMessage")]

            public string DeleteContactWarning

            {

                get

                {

                    return

                        "Confirm permanent removal of "  +

                    this.selectedContact.FirstName + " " + this.selectedContact.LastName;

                }

            }


    Unfortunately, this code segment is being executed before any Contact is selected. So i get a null exception message.
    This is how my ViewModel starts:

     [Export(ViewModelTypes.EmpContactViewModel)]   

        public class EmpContactViewModel: ViewModelBase, IPartImportsSatisfiedNotification

        {


    How can i delay the call to the DeleteContactWarning string till my SelectedContact has been set? 
    I tested the code after commenting out the SelectedContact names line, and i was able to get the custom message (minus the name, of course, to show in the  dialog)
    What am i doing wrong here?

    Thanks again
  • ppopadiyn

    RE: ModalDialogs, IEditableObject and MVVM in Silverlight 4


    posted by ppopadiyn on Sep 15, 2010 09:20

    Hi again J,

    Excellent case, you are doing nothing wrong. However there are several special features in mef, you should be familiar with. For example, when you call CompositionInitialized.SatisfyImport in your main control, this will create a new instance of your main viewmodel. On the next step, mef will try to satisfy all imports in your viewmodel. In your case probably it will need to create a new instance of the DeleteControl. Next, all imports in the DeleteControl will be satisfied. For example, your DeleteControl says: "Hey i want to import a string property to display on the UI" - this is DeleteContactWarning property. And of course, mef will try to find the corresponding export, which is located in your view model. And considering that your don't have selectedContact, it will throw NullReferenceExpcetion.

    As you can note, all graph of objects are created immediately after the calling of SatisfyImport. If you have a long and complex graph of objects this can be expensive and unnecessary. In your case, this also is causing an exception. That's why mef supports so called lazy exports/imports. In order to use it all you need to do is to import an [System.Lazy<T>] instead of [T] directly.

    So, first in your ViewModel, instead of importing directly DeleteControl, you should have something like this:

    [Import( ModalViewNames.DeleteControlName )]
    public Lazy<IModalView> DeleteControl
    {
        get;
        set;
    }

    When you use the DeleteControl, you should write something like this

    this.DeleteControl.Value
    In this case the instantiation of the DeleteControl will be delayed, until this.DeleteControl.Value is invoked. I suggest to put breakpoint in the DeleteControl constructor in the both cases, to see the difference.

    Also you should slightly modify the logic about the constructing of the DeleteContactWarning message. Instead of import/export properties, you should import/export methods.

    First, in your view model, you should construct the message in a method instead of property:

    //[Export( "CustomDeleteMessage" )]
    //public string DeleteMessage
    //{
    //    get
    //    {
    //        return "Confirm permanent removal of " + this.SelectedPerson.Name;
    //    }
    //}
     
    [Export( "CustomDeleteMessage" )]
    public string GetDeleteMessage()
    {
        return "Confirm permanent removal of " + this.SelectedPerson.Name;
    }
    Next, in the DeleteControl, you should have something like this:
    public DeleteControl()
    {
        InitializeComponent();
     
        this.Loaded += ( s, a ) =>
        {
            if ( this.CustomDeleteMessageAction != null )
            {
                this.tbMessage.Text = this.CustomDeleteMessageAction();
            }
        };
    }
     
    //[Import( "CustomDeleteMessage" )]
    //public string CustomDeleteMessage
    //{
    //    set
    //    {
    //        this.tbMessage.Text = value;
    //    }
    //}
     
    [Import( "CustomDeleteMessage" )]
    public Func<string> CustomDeleteMessageAction
    {
        get;
        set;
    }
    Again I suggest to try both of the solutions (import/export properties and methods) and see the difference. When you have import/export properties, it won't throw an exception, but i won't work correctly. E.g. try to delete more than one record - the delete message won't be construct correctly. If you use methods, everything should be o.k.

    Hope this help.

  • -_-

    RE: ModalDialogs, IEditableObject and MVVM in Silverlight 4


    posted by J on Sep 16, 2010 02:35

    Thanks, Pencho. I was trying to sort the rest out by myself and not have to bother you again, but have just one question and some comments.

    1. Exactly where should the  this.DeleteControl.Value be placed?

    2. I get the following messages in the ModalDialogWorker class after changing the parameter IModal modalview to the Lazy<T> version:


    'System.Lazy<Framework.IModalView>' does not contain a definition for 'Closed' and no extension method 'Closed' accepting a first argument of type 'System.Lazy<Framework.IModalView>' could be found (are you missing a using directive or an assembly reference?)

    'System.Lazy<Framework.IModalView>' does not contain a definition for 'DataContext' and no extension method 'DataContext' accepting a first argument of type 'System.Lazy<Framework.IModalView>' could be found (are you missing a using directive or an assembly reference?)


    And in the OnDeleteContactCommandExecute block I get the following 2 messages:


    The best overloaded method match for 'Framework.IModalDialogWorker.ShowDialog<.....Contact>(Framework.IModalDialog, Framework.IModalView, ......Contact, System.Action<.......Contact>)' has some invalid arguments

    cannot convert from 'System.Lazy<Framework.IModalView>' to 'Framework.IModalView'

    PHEW! I do think i need to take some time to read up on MEF.  If it is too much bother for you, i can switch to another implementation that does not use MEF for now, and convert later down the road.

    Thanks !



  • -_-

    RE: ModalDialogs, IEditableObject and MVVM in Silverlight 4


    posted by J on Sep 17, 2010 01:24

    Hi Pencho,

    I looked through some articles on MEF and was able to get things working. 

    Thanks again for your efforts !

  • ppopadiyn

    RE: ModalDialogs, IEditableObject and MVVM in Silverlight 4


    posted by ppopadiyn on Sep 17, 2010 08:15

    Hi J,

    Happy to hear that you got things working ;)

  • -_-

    RE: ModalDialogs, IEditableObject and MVVM in Silverlight 4


    posted by Jim on Nov 17, 2010 23:06

    Hello,

    It's very impressive and thank you for sharing your work.

    My knowledge is Silverlight, MVVM is very limited.  Can you tell what is required to create the project "Framework".  Perhaps this is not target for beginner, if there is step by step instructions it will be wonderful.

    Thanks anyway.

  • ppopadiyn

    RE: ModalDialogs, IEditableObject and MVVM in Silverlight 4


    posted by ppopadiyn on Nov 18, 2010 09:54

    Hi Jim,

    Thank you for your interest in the article. The project Framework is nothing special. It just contains common (reusable) classes. The purpose is at some point this library (these classes) to be re-used easy from another Silverlight project.

  • -_-

    RE: ModalDialogs, IEditableObject and MVVM in Silverlight 4


    posted by Jim on Nov 19, 2010 22:11

    Hi ppopadiyn,

    Is there a VB version for download?  I tried to convert it from C# to VB but failed.

  • ppopadiyn

    RE: ModalDialogs, IEditableObject and MVVM in Silverlight 4


    posted by ppopadiyn on Nov 22, 2010 11:01

    Hi Jim,

    Sorry but I am not a VB guy :(. However I would recommend you to use the following C# to VB converter. I'm using the demo version whenever I need to convert code and it works pretty reliable.

  • -_-

    RE: ModalDialogs, IEditableObject and MVVM in Silverlight 4


    posted by Jim on Dec 02, 2010 17:37

    Hi ppopadiyn,

    Can you give me some tips if I wanted to incorporate RIA service in your sample, what are the areas I should touch?  How do I change PersonModel to pull data from domain service?

  • -_-

    RE: ModalDialogs, IEditableObject and MVVM in Silverlight 4


    posted by Jim on Dec 12, 2010 00:46

    Hi ppopadiyn,

    I cloned your logic and changed it to become an option on menu.  It worked fine first time entering the option but it crashed at "this.DataContext = value;" on the MainPage (which I changed to become an option instead of the startup page).  Do you have any idea how to fix it?  Thansk.

  • ppopadiyn

    RE: ModalDialogs, IEditableObject and MVVM in Silverlight 4


    posted by ppopadiyn on Dec 13, 2010 09:08

    Hi Jim,

    Sorry about the late reply. First about the RIA Services: Using RIA Services with my demo is not so difficult. All database (RIA service) calls should be placed in the PersonModel - the PersonModel should create a new instance (internally) of the RIA context and should expose methods for Load, Insert, Delete and Save. Here is the tricky moment,  in WCF RIA all exposed business objects implements IEditableObject automatically. All changes are tracked in an EntityChangeSet object, and the changes are submitted collectively when you call SubmitChanges method of your domain context (respectively rejected, when you call the RejectChanges method).

    About the second issue, I'm afraid I'm not able to help you unless I see some sample code.

  • -_-

    RE: ModalDialogs, IEditableObject and MVVM in Silverlight 4


    posted by moakley on Jan 11, 2011 16:48

    I need the edit view model to call a web service and return data when completed.  No problem with that.  From the view model I then need to check the results of the web service call and then close if the results are good.  So from the edit view model I somehow need to tell the view to close.  Suggestions? 

    Thanks, moakley

  • -_-

    RE: ModalDialogs, IEditableObject and MVVM in Silverlight 4


    posted by moakley on Jan 14, 2011 19:38

    Issue resolved using mediator.

    Thanks, moakley

  • eduaguiar281

    Re: ModalDialogs, IEditableObject and MVVM in Silverlight 4


    posted by eduaguiar281 on Jun 09, 2011 22:52

    Thanks, your article is very good and helped me a lot, it was exactly what I needed.Sorry my bad English, I had to appeal to the google translator. Maybe not 100% correct.

  • mobygeek

    Re: ModalDialogs, IEditableObject and MVVM in Silverlight 4


    posted by mobygeek on Jun 16, 2011 14:06

    clean work! excellent! could this be applied also for WPF and I mainly worked with DI frameworks (autofac) is there any particular reason you preffered MEF over DI Framerwork? as far as I know MEF's primary role is not about DI.

    thanks

  • geertd

    Re: ModalDialogs, IEditableObject and MVVM in Silverlight 4


    posted by geertd on Dec 04, 2011 13:16

    Suppose that you are working with a hierarchical structure e.g. a Company class that has a list of Persons. You select a Company from a list, open up a modal dialog to edit that company and start editing it: change the Company.Name, change the name of the first person of that company, add a new person... and then you want to cancel.

    I don't think the generic implementation of IEditableObject handles such a hierarchy?

    How would you handle such a case?




  • ppopadiyn

    Re: ModalDialogs, IEditableObject and MVVM in Silverlight 4


    posted by ppopadiyn on Dec 04, 2011 15:22
    Hi geertd, you are right, the IEditableObject implemenation will not work in that scenario. It will require additional work to handle hierarchies.
  • geertd

    Re: ModalDialogs, IEditableObject and MVVM in Silverlight 4


    posted by geertd on Dec 04, 2011 15:40

    Maybe you don't need to:

    Another approach is to break this problem down into more manageable pieces:

    - A CompanyList VM and V for the company list
    - A Company VM and V for handing the Company properties and the list of EditableEmployees (Employee wrapped in generic IEditableObject)
    - A Employee VM and V for handling the Employee properties

    The CompanyViewModel could have commands to add/remove/edit employees and these would create a EmployeeViewModel (and calling BeginEdit on the Employee in case of edit) and use that as datacontext of an EmployeeView shown in a model dialog, and handle the result of the modal dialog (calling CancelEdit on cancel, or EndEdit on OK)...

Add Comment

Login to comment:
  *      *