Jason D
Jason D

Reputation: 2664

WPF & MVVM Light- Pass object into new window

I would like to learn the most proper way to go about this: I have a Listview in my GameView that is bound to an ObservableCollection<Adventurer>. Upon double-clicking on a cell, I need a new window (or something else if anything is more appropriate) to open and display data about the correct Adventurer according to the cell. So far I haven't been able to. This is what I have so far (it's not much, but nothing I've tried has worked).

The trigger/command in my ListView in GameView.xaml

<i:Interaction.Triggers>
    <i:EventTrigger EventName="MouseDoubleClick">
        <cmd:EventToCommand Command="{Binding Mode=OneWay, Path=ShowAdvCommand}"
                            CommandParameter="{Binding ElementName=AdvListView, 
                                                       Path=SelectedItem}"
                            PassEventArgsToCommand="True" />
    </i:EventTrigger>
</i:Interaction.Triggers>

And the command in GameViewModel.cs

ShowAdvCommand = new RelayCommand<Adventurer>(p =>
{
    System.Windows.MessageBox.Show(p.Name);
});

The MessageBox is just there to confirm that Eventtocommand was working.

I essentially need a container that will take in the correct Adventurer as a parameter after double-clicking a Listview cell and allow me to display data specific to that instance. I would also prefer to stick to something MVVM-friendly.

Any advice would be greatly appreciated.

Update: I may have made a little progress:

GameViewModel:

ShowAdvCommand = new RelayCommand<Adventurer>(p =>
{
    AdventurerView adv = new AdventurerView(p);
    adv.Show();
});

AdventurerView:

public partial class AdventurerView : Window
{
    Adventurer adv;

    public AdventurerView(Adventurer adv)
    {
        this.adv = adv;
        InitializeComponent();
    }
}

Now I need to figure out how to make this work in XAML, databinding and such.

Update: ...and then I realized that this completely goes against MVVM. Does anybody have any advice?

Update: Would MVVM Light's messenger help me here? I've been tinkering with it but haven't gotten it to work.

Update: This question is still up in the air. I tried the Prism approach but there was some conflict between Prism and MVVM Light that caused more trouble than it was worth. I'm open to any ideas that are compatible with MVVM Light and the MVVM pattern in general.

Update: Also, I would like to do this in a way where multiple popups can exist concurrently, if possible.

Upvotes: 2

Views: 2720

Answers (3)

Alyce
Alyce

Reputation: 896

In a similar situation, I've used MvvmLight's Messenger, and it worked really well. On double click, send a message from your viewmodel containing the entity you want to pass. Somewhere you will need to register to receive the message, depending on how you have set up your views and viewmodels to be activated.

You could register to receive the message in your MainPage.xaml, and either pass the entity straight to the view's constructor, or access the view's DataContext via an interface to pass the entity, depending on whether you're using a viewmodel in you childwindow. E.g.

AdventurerView adv = new AdventurerView();
IEntityViewModel vm = adv.DataContext as IEntityViewModel;
vm.SetCurrentEntity(entity);
adv.Show();

The IEntityViewModel might look like the following:

public interface IEntityViewModel<T> where T : class
{
    void SetCurrentEntity(T entity);
}

The viewmodel would implement this interface:

public class AdventurerViewModel : IEntityViewModel<Adventurer>
{
    public void SetCurrentEntity(Adventurer entity)
    {
        // Do what you need to with the entity - depending on your needs, 
        // you might keep it intact in case editing is cancelled, and just
        // work on a copy.
    }
}

Upvotes: 5

Thelonias
Thelonias

Reputation: 2935

This is a nice case for Prism's InteractionRequest. Essentially, you have an InteractionRequest object on your ViewModel that you raise when you double click (inside your double click command). Your view has an Action on it that handles the Raised event and shows the new view. You pass a new ViewModel to that interaction and that's the DataContext for the window that'll display. Here's some good information to get you started. This is how I display all child windows in my application.

Upvotes: 0

bryanbcook
bryanbcook

Reputation: 17993

As you've pointed out, proper MVVM wouldn't instantiate the view and pass the view model in through the constructor. You'd be better off binding the ViewModel to the View and there are many different ways of doing it.

One pattern that has emerged is a concept known as a "screen conductor". This is a top level ViewModel or controller that handles which ViewModel represents the main window. Again, many different ways to do this. For example, the ViewModel could raise a standard .net event that the Screen Conductor handles. You could use an message passing system like Caliburn.Micro's EventAggregator or MVVM Light's Messenger. I think MEFedMVVM also has an event aggregator to accomplish this as well.

Caliburn.Micro also has a WindowManager that you can pass in your ViewModel and have it automatically find the corresponding View and manage the window lifetime.

Lots of options. Find the one that works the best for you.

Upvotes: 0

Related Questions