tags:

views:

682

answers:

2

Just like this question, I'm learning MVVM using the sample created by Josh Smith and I wanted to add a functionality of update.

Two problems arise (not addressed in the referred question):

  1. What is the best and light way to create a workspace from another workspace?
  2. How to find if there is already a workspace editing the same customer.
+1  A: 

Using Josh's MvvmFoundation, you can use Messenger to pass messages between ViewModels...

I would do it something like this:

-1. Create a singleton instance of Messenger that can be seen from anywhere within your app.

public class MyMessenger
{
   static Messenger _messenger;
   public static Messenger Messenger
   {
      get
      {
         if (_messenger == null)
            _messenger = new Messenger();
         return _messenger;
      }
   }
}

-2. Then in your MainWindowViewModel, register for notifications of a specific message - you can easily define your own message types, e.g.

MyMessenger.Messenger.Register<ViewModelBase>("CreateNew", (param) =>
{
  DoWork(param); /* If memory serves, the MainWindowViewModel already has the logic to create a new CustomerViewModel and put it in your Workspaces collection... (which I think answers your second point)  */
});

-3. Finally, you need to 'notify' this message from within your originatin ViewModel,

simply:

MyMessenger.Messenger.NotifyColleagues("CreateNew", new CustomerViewModel(customerNo));

Sorry - I can't remember the exact structure of the CustomerViewModel off the top of my head, but I hope you get the idea :)

Hope this helps :) Ian

IanR
Yes, it helps a lot. I was thinking in making MainWindowViewModel a singleton, but I think this works better.
Eduardo Molteni
I have trouble in point 2 since VB does not support anonymous methods/lambda expressions with a statement body and the 3rd point is little vague. I'm placing a bounty on the question.
Eduardo Molteni
+1  A: 

There is a lot of things going on here. There are two parts to your question (if I'm not missing anything).

  1. How to forward a double-click action to a Command in your ViewModel
  2. How to open a "workspace" in this sample (a workspace is just a tab, in this case).

How to DoubleClick a ListViewItem, MVVM Style

There are a number of ways, but this question on SO sums it up pretty well: http://stackoverflow.com/questions/1035023/firing-a-double-click-event-from-a-wpf-listview-item-using-mvvm

I personally use MarlonGrech's attached behaviors, so I will show you how to do that:

<ListView 
      AlternationCount="2" 
      DataContext="{StaticResource CustomerGroups}"
      ...>
      <ListView.ItemContainerStyle>
           <Style TargetType="{x:Type ListViewItem}">
                <Setter Property="acb:CommandBehavior.Event"
                        Value="MouseDoubleClick" />
                <Setter Property="acb:CommandBehavior.Command"
                        Value="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}, Path=DataContext.EditCustomerCommand}" />
                <Setter Property="acb:CommandBehavior.CommandParameter"
                        Value="{Binding}" />
            </Style>
      </ListView.ItemContainerStyle>
</ListView>

This would bind to your EditCustomerCommand (which would be a RelayCommand) that you would need to setup in your AllCustomersViewModel.

Add a New Workspace

This one is more tricky. You'll notice that MainWindowViewModel has a AddWorkspace, but we really don't have a reference to that ViewModel from AllCustomersViewModel.

I decided the best way to do this (for me) would be to create another interface called "IWorkspaceCommands" that AllCustomersViewModel would use to create new workspaces. This is mainly to contrast with the previous answerer's suggestion of the Messenger approach. If you prefer the Messenger approach, you can use that here instead.

MainWindowViewModel would actually implement this and pass itself in when it created AllCustomersViewModel.

public interface IWorkspaceCommands
{
     void AddWorkspace(WorkspaceViewModel view);
}

And here is the basic implementation of the interface. This includes a check to see if the view is already open, as requested (really simple!):

#region IWorkspaceCommands Members

public void AddWorkspace(WorkspaceViewModel view)
{
    if (!Workspaces.Contains(view))
    {
        Workspaces.Add(view);
    }
    SetActiveWorkspace(view);
}

#endregion

And finally, here is that RelayCommand I was telling you about in your AllCustomersViewModel (along with some constructor modifications):

IWorkspaceCommands _wsCommands;
public AllCustomersViewModel(CustomerRepository customerRepository, IWorkspaceCommands wsCommands)
{
    _wsCommands = wsCommands;
    EditCustomerCommand = new RelayCommand(EditCustomer);
    ...
}

public void EditCustomer(object customer)
{
    CustomerViewModel customerVM = customer as CustomerViewModel;
    _wsCommands.AddWorkspace(customerVM);

}

That's pretty much it. Because you are dealing with a reference to the same ViewModel that was used to create the AllCustomersViewModel, when you edit it in one screen, it updates in the other without eventing or messaging (nice!, but probably not robust enough).

There's a slight problem with the ComboBox not auto-selecting the value for company/person, but that is left as an exercise for the reader.

As part of my package today, I'm including a fully functional demo at no extra charge. http://dl.getdropbox.com/u/376992/MvvmDemoApp.zip

Hope this helps,

Anderson

Anderson Imes
Man, you get the bounty, super-complete answer with code! excellent!.
Eduardo Molteni
Regarding Checking if the view is already open, I'm afraid that is not that simple. I will have to check if there is already the *same* customer open (you can have multiple customers open, but only one instance of the same customer). I think I will have a dictionary with the entity name and the ID.
Eduardo Molteni
This code does check if the same customer is open. It will be the same object instance in this case. You might need to do this if you decide to edit a different copy of the customerviewmodel, but in this case you are passing around the same object so Contains returns the right value.Good luck.
Anderson Imes