Let the ViewModel know the generic object of the V

2019-04-16 17:01发布

问题:

I have a generic view where I "inject" some specific view into a contained ContentControl (I created that feature with these help -> help 1 - help 2).

The basic source of my views are these:

MyGenericView.xaml

<UserControl x:Class="MyNS.MyGenericView"
             ... >
    <UserControl.Resources>
        <vml:ViewModelLocator x:Key="Locator" d:IsDataSource="True" />
    </UserControl.Resources>
    <Grid DataContext="{Binding MyGenericViewModel, Source={StaticResource Locator}}">
        <ContentControl Content="{Binding MyObject}" />
    </Grid>
</UserControl>

CustomerView.xaml

<UserControl x:Class="AnotherNS.CustomerView"
             ... >
    <Grid>
        <StackPanel Orientation="Vertical">
            <Label Content="Description" />
            <TextBox Text="{Binding description}" />
        </StackPanel>
    </Grid>
</UserControl>

Crud.xaml: a resource dictionary which I use to "solve" what's the correct view to show, depending on the DataType of MyObject object provided by the generic view.

<ResourceDictionary ... >
    <DataTemplate DataType="{x:Type mo:Customer}">
        <vw:CustomerView />
    </DataTemplate>
    <DataTemplate DataType="{x:Type mo:Product}">
        <vw:ProductView />
    </DataTemplate>
    ...
</ResourceDictionary>

It's working fine. I can manage MyObject through the "specific" view (customer, product, etc).

Well. That's my problem:

All the specific views have their own ViewModels and, of course, they manage the respective views' data. But I don't know (on the viewmodel) what's the object (MyObject) I'm working with, because the Generic View provides it to the Specific View, not to the viewmodel.

Is there a way to let the ViewModels of the specific Views know the object which is "commanding" the view?

回答1:

In MVVM Light you would send a publisher/subscriber style broadcast message that would allow you to communicate with the "hosting" view model without having to have a hard reference to the "hosting" control in the "hosted" control.

This allows the "hosted" control to stay decoupled from the "hosting" control and yet communicate with between the two.

EDIT:

In MVVM Light there's a messenger object that takes care of a lot of the details for you. You can create message classes that keep different messages and the arguments they send seperate. You can also specify a "Token" just a specific string (I usualy setup a set of constants in a class to house my various "Tokens") that allows only the subsribers to that message and that token to receive the message. I've included a code sample below of what I use in MVVM Light in v3 of MVVM Light you need to make sure to unregister from the message because it does not use the weak eventing pattern.

If you didn't want to use MVVM Light you can use the same idea in your publisher/subscriber model, you'd just have to do the work on determining if the token the publisher sent is the token the subscriber is looking for on your own

public static class DescriptiveMessageName
{
public static void Send(object args)
{
    Messenger.Default.Send(args, "SpecificToken");
}

public static void Send(object args, string token)
{
    Messenger.Default.Send(args, token);
}

public static void Register(object recipient, Action<object> action)
{
    Messenger.Default.Register(recipient,
              "SpecificToken", action);
}

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

public static void UnRegister(object recipient)
{
    Messenger.Default.Unregister<object>(recipient);
}

public static void UnRegister(object recipient, Action<object> action)
{
    Messenger.Default.Unregister<object>(recipient, action);
}

}