C# 将委托/命令作为参数传递给MVVM中的另一个命令

C# 将委托/命令作为参数传递给MVVM中的另一个命令,c#,wpf,xaml,mvvm,delegates,C#,Wpf,Xaml,Mvvm,Delegates,我有一个用户控件“a”,它的子控件是另一个用户控件“B”。用例场景是,当用户单击“a”上的按钮时,它应该请求“B”处理一些信息(通常与“B”的状态相关)并获得结果,然后将在“a”中的某些计算中使用 现在,我正在使用MVVM模式。“A”的viewmodel没有对A或B的UI的任何引用。我正在寻找这一点:当用户在“A”的UI上点击按钮时,我需要调用“B”中的命令,方法是将“A”的viewmodel中的委托作为参数传递给该命令。这样,当“B”中的命令处理信息时,它就可以调用从“A”传递的回调函数 现在

我有一个用户控件“a”,它的子控件是另一个用户控件“B”。用例场景是,当用户单击“a”上的按钮时,它应该请求“B”处理一些信息(通常与“B”的状态相关)并获得结果,然后将在“a”中的某些计算中使用

现在,我正在使用MVVM模式。“A”的viewmodel没有对A或B的UI的任何引用。我正在寻找这一点:当用户在“A”的UI上点击按钮时,我需要调用“B”中的命令,方法是将“A”的viewmodel中的委托作为参数传递给该命令。这样,当“B”中的命令处理信息时,它就可以调用从“A”传递的回调函数

现在,委托是无参数的。一旦usercontrol“B”中的命令处理了一些信息,它就会更新其状态。此状态是一个依赖属性,它将绑定到“a”的viewmodel中的属性

有没有办法做到这一点

我目前有一个可行的解决方案。但我想即兴创作代码,让它变得更好。这是我目前解决此问题的临时方法:

当用户单击“a”中的按钮时,我在“a”(xaml.cs)中有一个事件处理程序。在事件处理程序中,由于我有“B”的引用,我正在检查“B”中的命令是否可以执行,然后调用它。然后,我检查命令(在“A”的viewmodel中)是否可以执行,然后调用它

如果我的问题不够描述性,下面是一个代码示例来支持它:

A'的XAML:

<bNameSpace:B x:Name="BObject" DepenPropertyInB="{Binding PropInA, Mode=OneWayToSource}"/>
....
<telerik:RadButton Content="Process" HorizontalAlignment="Right" Height="30" Width="80" Grid.Column="2" Click="ProcessButton_Click" />
private void ProcessButton_Click(object sender, RoutedEventArgs e)
{
    if (BObject.StateDataRequestedCommand.CanExecute(""))
        BObject.StateDataRequestedCommand.Execute("");

    if(ViewModel.ProcessInfoRequestedCommand.CanExecute(""))
        ViewModel.ProcessInfoRequestedCommand.Execute("");
}
private void OnProcessInfoRequestedCommand(string anyValue)
{
    // read the value of PropInA and do some processing
}
private void OnStateDataRequestedCommand(string anyValue)
{
    // Bases on B's state do some calc and update DepenPropertyInB
}
视图A的模型:

<bNameSpace:B x:Name="BObject" DepenPropertyInB="{Binding PropInA, Mode=OneWayToSource}"/>
....
<telerik:RadButton Content="Process" HorizontalAlignment="Right" Height="30" Width="80" Grid.Column="2" Click="ProcessButton_Click" />
private void ProcessButton_Click(object sender, RoutedEventArgs e)
{
    if (BObject.StateDataRequestedCommand.CanExecute(""))
        BObject.StateDataRequestedCommand.Execute("");

    if(ViewModel.ProcessInfoRequestedCommand.CanExecute(""))
        ViewModel.ProcessInfoRequestedCommand.Execute("");
}
private void OnProcessInfoRequestedCommand(string anyValue)
{
    // read the value of PropInA and do some processing
}
private void OnStateDataRequestedCommand(string anyValue)
{
    // Bases on B's state do some calc and update DepenPropertyInB
}
视图B的模型:

<bNameSpace:B x:Name="BObject" DepenPropertyInB="{Binding PropInA, Mode=OneWayToSource}"/>
....
<telerik:RadButton Content="Process" HorizontalAlignment="Right" Height="30" Width="80" Grid.Column="2" Click="ProcessButton_Click" />
private void ProcessButton_Click(object sender, RoutedEventArgs e)
{
    if (BObject.StateDataRequestedCommand.CanExecute(""))
        BObject.StateDataRequestedCommand.Execute("");

    if(ViewModel.ProcessInfoRequestedCommand.CanExecute(""))
        ViewModel.ProcessInfoRequestedCommand.Execute("");
}
private void OnProcessInfoRequestedCommand(string anyValue)
{
    // read the value of PropInA and do some processing
}
private void OnStateDataRequestedCommand(string anyValue)
{
    // Bases on B's state do some calc and update DepenPropertyInB
}

我稍后会回来,并为其添加更多细节,但我认为基本上需要这样做

单击应该有一个
DelegateCommand
类型实现,其中包含对a和B的视图模型的引用。您需要某种“导体”类来实例化视图模型,并且可以将适当的引用传递到命令对象中


因此,很明显,该命令属于A的视图模型,但是,它需要对B的引用。因此,请记住设置命令类:

public MyCommand : ICommand
{
    private readonly ViewModelA _a, ViewModelB _b;

    public MyCommand(ViewModelA a, ViewModelB b)
    {
        _a = a;
        _b = b;
    }

    public bool CanExecute(object arg)
    {
        // can-execute logic here
    }

    public void Execute(object arg)
    {
        _b.StateDataRequestedCommand.Execute("");
        _a.ProcessInfoRequestedCommand.Execute("");
    }
}
现在,将MyCommand的一个实例放入A的视图模型中

public MyCommand StateRequestedCommand { get; set; }
像平常一样和它绑定

<telerik:RadButton Command="{Binding StateRequestedCommand}" />


现在,挑战在于如何在视图模型中实例化这个类。我通常使用NInject向类中注入外部依赖项,但有不同的方法。无论哪种方式,它都必须发生在比ViewModelA本身更高的级别上,因为它没有对ViewModelB的引用。这可能在应用程序启动逻辑中,或者在IoC(例如NInject)绑定中,或者在视图模型“定位器”中,如果您正在使用的话。

我稍后会回来并添加更多细节,但我认为基本上需要这样做

单击应该有一个
DelegateCommand
类型实现,其中包含对a和B的视图模型的引用。您需要某种“导体”类来实例化视图模型,并且可以将适当的引用传递到命令对象中


因此,很明显,该命令属于A的视图模型,但是,它需要对B的引用。因此,请记住设置命令类:

public MyCommand : ICommand
{
    private readonly ViewModelA _a, ViewModelB _b;

    public MyCommand(ViewModelA a, ViewModelB b)
    {
        _a = a;
        _b = b;
    }

    public bool CanExecute(object arg)
    {
        // can-execute logic here
    }

    public void Execute(object arg)
    {
        _b.StateDataRequestedCommand.Execute("");
        _a.ProcessInfoRequestedCommand.Execute("");
    }
}
现在,将MyCommand的一个实例放入A的视图模型中

public MyCommand StateRequestedCommand { get; set; }
像平常一样和它绑定

<telerik:RadButton Command="{Binding StateRequestedCommand}" />


现在,挑战在于如何在视图模型中实例化这个类。我通常使用NInject向类中注入外部依赖项,但有不同的方法。无论哪种方式,它都必须发生在比ViewModelA本身更高的级别上,因为它没有对ViewModelB的引用。这可能在应用程序启动逻辑中,或者在IoC(如NInject)绑定中,或者在视图模型“定位器”中,如果您正在使用的话。

每个命令都需要CommandParameter,您使用它,但发送“”而不是实际参数。 参数可以是任意类型,因为它作为对象被清除。因此,您可以将任何对象作为参数发送到任何命令

class CommandA : ICommand
{
  void Execute(object param)
  {
    var commandB = param as CommandB;
    if(commandB != null)
    {
      commandB.Execute((int a) => {
          // continue your code here
        });
    }
  }

  bool CanExecute(object param)
  {
    return param is CommandB;
  }
}

class CommandB : ICommand
{
  void Execute(object param)
  {
    var action = param as Action<int>;
    if(action != null)
    {
      action(10);
    }
  }
}

class MVA
{
  CommandA CommandA{get;}
  CommandB CommandB{get;}
}
class命令a:ICommand
{
无效执行(对象参数)
{
var commandB=参数作为commandB;
if(commandB!=null)
{
commandB.Execute((int a)=>{
//在这里继续你的代码
});
}
}
布尔CanExecute(对象参数)
{
返回参数为CommandB;
}
}
类命令B:ICommand
{
无效执行(对象参数)
{
var action=参数as action;
如果(操作!=null)
{
行动(10);
}
}
}
MVA级
{
CommandA CommandA{get;}
CommandB CommandB{get;}
}
XAML:



每个命令都需要CommandParameter,您可以使用它,但发送“”而不是实际参数。 参数可以是任意类型,因为它作为对象被清除。因此,您可以将任何对象作为参数发送到任何命令

class CommandA : ICommand
{
  void Execute(object param)
  {
    var commandB = param as CommandB;
    if(commandB != null)
    {
      commandB.Execute((int a) => {
          // continue your code here
        });
    }
  }

  bool CanExecute(object param)
  {
    return param is CommandB;
  }
}

class CommandB : ICommand
{
  void Execute(object param)
  {
    var action = param as Action<int>;
    if(action != null)
    {
      action(10);
    }
  }
}

class MVA
{
  CommandA CommandA{get;}
  CommandB CommandB{get;}
}
class命令a:ICommand
{
无效执行(对象参数)
{
var commandB=参数作为commandB;
if(commandB!=null)
{
commandB.Execute((int a)=>{
//在这里继续你的代码
});
}
}
布尔CanExecute(对象参数)
{
返回参数为CommandB;
}
}
类命令B:ICommand
{
无效执行(对象参数)
{
var action=参数as action;
如果(操作!=null)
{
行动(10);
}
}
}
MVA级
{
CommandA CommandA{get;}
CommandB CommandB{get;}
}
XAML:



我有兴趣了解更多有关此解决方案的信息,请回来详细说明。@Shankar我更新了我的答案。也许还有点模糊,但我希望它在某种程度上有所帮助。谢谢dbaseman,这是一个很好的解决方案,使用Prism,视图模型可以很容易地注入。我有兴趣了解更多关于这个解决方案的信息,所以请回来详细说明。@Shankar我更新了我的答案。也许还是有点模糊,但我希望它能在某种程度上有所帮助。谢谢DBASMAN,这是一个很好的解决方案