将自定义类型作为命令参数传递

Pass custom type as command parameter

我必须使用 MVVM Light 框架将一个旧的 Delphi 应用程序迁移到 WPF,在我的主屏幕上我有大约 50 MenuItems...(无评论)。

目前,每个 MenuItem 都有一个 RelayCommand 执行基于精确模型的泛型方法:

<MenuItem Command="{Binding ShowOrderCommand}"/>

在 ViewModel 中

ShowOrderCommand = new RelayCommand(ShowGridType<OrderModel>, CanShowGridType)

(其中 OrderModel 接口是 IBaseModel)调用此方法定义:

ShowGridType<T>() where T : IBaseModel

(注意:必须保留泛型,因为在代码中有进一步的 DI 解析)。

我想通过一个命令删除这数百行 RelayCommand<OrderModel, PriceModel, ...> 实例化,该命令可以通过命令参数(或其他)使用通用方法(如 ShowCommand = RelayCommand<IBaseModel> 传递模型类型)或 RelayCommand<TModel>.

我以为我只是通过这个找到了解决方案:

<MenuItem Command="{Binding ShowCommand}"  CommandParameter="{x:Type models:OrderModel}"/>
ShowCommand = RelayCommand<IBaseModel>(ShowGridType);
ShowGridType<Tmodel>(Tmodel model) where Tmodel : IBaseModel

但是当我点击 MenuItem 时出现转换错误:

Unable to cast object of type 'System.RuntimeType' to type 'XXXXXX.Models.IBaseModel'

我也曾尝试接收 'object' 而不是 IBaseModel,但找不到如何将其与通用定义 ShowGridType<T> 一起使用。

有什么有用的想法吗?

I use Unity as container and I only need the type to resolve many things [...]

如果它只是关于分辨率,您可以公开带有 Type 作为参数的命令。

public RelayCommand<Type> ShowOrderCommand { get; }

为执行创建方法并可以执行委托。

private bool CanShowOrder(Type type)
{
   // ...replace with your code.
   return true;
}

private void ShowOrder(Type type)
{
   // Resolve type using the your container here.
   var obj= _myUnityContainer.Resolve(type);

   // ...do something with the instance.
}

如您所见,Unity 不仅提供了通用的解析扩展方法,还有其他方法可以与 Type 一起使用,请参见下文。这意味着您不需要在视图模型中使用通用方法。

使用两种方法初始化 ShowOrderCommand 命令。

ShowOrderCommand = new RelayCommand<Type>(ShowOrder, CanShowOrder);

在 XAML 中,像您在问题中所做的那样绑定模型作为每个 MenuItem.

的命令参数
<MenuItem Command="{Binding ShowCommand}"  CommandParameter="{x:Type models:OrderModel}"/>

现在,如果你真的需要用特定类型调用你自己的泛型方法,有两种选择。

  1. 使用 ifswitch 语句显式检查类型以调用通用 ShowGridType<T> 方法。

    private void ShowOrder(Type type)
    {
       if (type == typeof(OrderModel))
          ShowGridType<OrderModel>();
    
       // ...check other derivatives.
    }
    
  2. Reflect the method and type parameter and call it. Here, you get the method info using GetMethod 并使用您拥有的具体 Type 作为它的类型参数。然后在 this 上调用它,因为该方法是在包含 class 中定义的,并且没有参数 (null).

    private void ShowOrder(Type type)
    {
       var method = GetType().GetMethod(nameof(ShowGridType), BindingFlags.Instance | BindingFlags.NonPublic);
       var genericMethod = method.MakeGenericMethod(type);
    
       genericMethod.Invoke(this, null);
    }
    

    请注意您有一些陷阱,所以这只是一个概念验证。

这两种方法都涉及到反射,因为你需要检查运行时类型。将 IBaseModel 作为命令参数传递不会在运行时神奇地调用具有具体类型的泛型方法。这也意味着有一定的开销,在运行时可能会出错,而代码编译得很好,所以你需要确定你在做什么,仔细检查代码。