MVVM: Editing a Customer. How to find a workspace with the same object and to create a new workspace from another workspace
Just like this question, I'm learning MVVM using the s开发者_JAVA技巧ample created by Josh Smith and I wanted to add a functionality of update.
Two problems arise (not addressed in the referred question):
- What is the best and light way to create a workspace from another workspace?
- How to find if there is already a workspace editing the same customer.
There is a lot of things going on here. There are two parts to your question (if I'm not missing anything).
- How to forward a double-click action to a Command in your ViewModel
- 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: 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
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
精彩评论