MVVM Light转换消息

时间:2012-03-28 10:55:24

标签: c# silverlight mvvm mvvm-light

我有以下行为:

    public class NavigateAndBroadcastAction : NavigateToPageAction
        {
            protected override void Invoke(object parameter)
            {            
                base.Invoke(parameter);
                Messenger.Default.Send<NavigatingMessage<ViewModelBase>>(new NavigatingMessage<ViewModelBase>(this, PassedObject), NavigationToken);
            }

            public ViewModelBase PassedObject
            {
                get { return (ViewModelBase)GetValue(PassedObjectProperty); }
                set { SetValue(PassedObjectProperty, value); }
            }

            // Using a DependencyProperty as the backing store for PassedObject.  This enables animation, styling, binding, etc...
            public static readonly DependencyProperty PassedObjectProperty = DependencyProperty.Register("PassedObject", typeof(ViewModelBase), typeof(NavigateAndBroadcastAction), new PropertyMetadata(null)); 
...
    }

它基本上使用NavigateToPageAction(也可以在Blend中使用),但允许我也广播一个ViewModel对象(我用它来从List页面导航到Detail页面并传递所选对象)

Xaml看起来像这样:( PassedObject Binding是一个继承自ViewModelBase的DetailViewModel实例)

<i:Interaction.Triggers>
     <i:EventTrigger EventName="MouseLeftButtonDown">
         <b:NavigateAndBroadcastAction TargetPage="/View/SubjectDetailPage.xaml" NavigationToken="SubjectDetailNavigationToken" PassedObject="{Binding}" />
     </i:EventTrigger>
</i:Interaction.Triggers>

现在,我想注册消息:

Messenger.Default.Register<NavigatingMessage<DetailViewModel>>(this, NavigationToken, true, Action);

但那不起作用。有效的方法是注册NavigatingMessage<ViewModelBase>,然后将收到的消息转发给NavigatingMessage<DetailViewModel>。有办法吗?

可以这样做,以便信使检测到正在发送的对象的实际类型并正确传递给那些为该类型注册的对象吗?

3 个答案:

答案 0 :(得分:2)

一种可能的方法是使用反射来发送消息,方法是在运行时创建具有正确泛型类型的消息 另一种方法是使用dynamic并输入推理:

protected override void Invoke(object parameter)
{            
    base.Invoke(parameter);
    dynamic viewModel = PassedObject;
    Messenger.Default.Send(GetMessage(this, viewModel), NavigationToken);
}

private NavigatingMessage<T> GetMessage<T>(NavigateToPageAction action, T item)
{
    return new NavigatingMessage<T>(action, item);
}

使用反射的版本有点混乱:

protected override void Invoke(object parameter)
{            
    base.Invoke(parameter);
    Send(PassedObject, NavigationToken);
}

void Send(ViewModelBase objectToSend, string navigationToken)
{
    var genericMessageType = typeof(NavigatingMessage<>)
    var viewModelType = objectToSend.GetType();
    var messageType = genericMessageType.MakeGenericType(viewModelType);
    var message = Activator.CreateInstance(messageType, this, objectToSend);

    var method = typeof(Messenger).GetMethods()
                                  .Single(x => x.Name == "Send" &&
                                               x.GetParameters().Count() == 2 &&
                                               x.GetParameters()
                                                .First()
                                                .ParameterType
                                                .GetGenericTypeDefinition()
                                                 == genericMessageType);
    method.MakeGenericMethod(viewModelType)
          .Invoke(Messenger.Default, new [] { message, navigationToken });
}

此代码假定NavigationTokenstring。如果不是,只需更改Send方法的第二个参数的类型。如果Messenger仅包含Send方法的一次重载,则可以简化Single中的条件。另一方面,如果该方法存在大量重载,则可能需要对其进行优化。

答案 1 :(得分:0)

如果您将邮件发送为Messenger.Default.Send<NavigatingMessage<DetailViewModel>>(new NavigatingMessage<DetailViewModel>(this, PassedObject),则可以根据需要收到邮件,而无需投放。

答案 2 :(得分:0)

这是当前版本的MVVM Light的限制。我正在考虑将来改进它,但它非常棘手......