使用 MVVM 在另一个控件中执行方法

Execute a method in another Control using MVVM

我构建了一个虚拟 UserControl,它的代码隐藏中有一个方法来显示消息!我在主 window 中使用了此控件,并希望在使用命令和 MVVM 单击 Button 时执行其方法。我该怎么做?

<UserControl x:Class="ControlBining.Control1"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <Grid DataContext="{Binding RelativeSource={RelativeSource AncestorType=UserControl}}">
    </Grid>
</UserControl>

C#:

public partial class Control1 : UserControl
   {
      public Control1()
      {
         InitializeComponent();
      }

      public void ShowMessage()
      {
         MessageBox.Show("Called from other control!");
      }
   }

主窗口XAML:

<Window x:Class="ControlBining.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:ControlBining"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Window.DataContext>
        <local:ViewModel/>
    </Window.DataContext>
    <StackPanel Margin="0 50 0 0">
        <local:Control1 Width="100"/>
        <Button Width="100" Content="Show Message"/>
    </StackPanel>
</Window>

中继命令:

   public class RelayCommand : ICommand
   {
      private readonly Predicate<object> m_canExecute;
      private readonly Action<object> m_execute;

      public RelayCommand(Predicate<object> canExecute, Action<object> execute)
      {
         m_canExecute = canExecute;
         m_execute = execute;
      }

      public event EventHandler CanExecuteChanged
      {
         add => CommandManager.RequerySuggested += value;
         remove => CommandManager.RequerySuggested -= value;
      }

      public bool CanExecute(object parameter)
      {
         return m_canExecute(parameter);
      }

      public void Execute(object parameter)
      {
         m_execute(parameter);
      }
   }

目前,我已经让它工作了,但我真的不确定它是否是一个好的设计:

控制代码隐藏

  private void Control1_Loaded(object sender, RoutedEventArgs e)
  {

     ViewModel m = (ViewModel)DataContext;
     m.ShowMessage += M_ShowMessage;
  }

  private void M_ShowMessage()
  {
     ShowMessage();
  }

在 ViewModel 中

  public event Action ShowMessage;

  private ICommand m_showMessageCommand;
  public ICommand ShowMessageCommand
  {
     get
     {
        return m_showMessageCommand ?? (m_showMessageCommand = new RelayCommand(
                  p => true,
                  p => ShowMessage?.Invoke()));
     }
  }

XAML:

如果您只需要显示一条消息,您应该将 ShowMessage() 方法移动到视图模型并使用消息服务从视图模型执行此操作 class.

如果你真的想调用一些只有在视图中定义才有意义的方法,这可以通过在视图中实现一个接口并用这个接口注入视图模型来完成。例如,当您调用命令时:

public interface IView
{
    void ShowMessage();
}

public partial class Control1 : UserControl, IView
{
    public Control1()
    {
        InitializeComponent();
    }

    public void ShowMessage()
    {
        MessageBox.Show("Called from other control!");
    }
}

查看模型:

public ICommand ShowMessageCommand
{
    get
    {
        return m_showMessageCommand ?? (m_showMessageCommand = new RelayCommand(
                  p => true,
                  p =>
                  {
                      IView view as IView;
                      if (view != null)
                      {
                          //...
                          view.ShowMessage();
                      }
                  }));
    }
}

视图模型对视图一无所知,它只知道一个接口,该接口当然可以称为 IView

另一种选择是使用事件聚合器或信使以松散耦合的方式将事件或消息从视图模型发送到视图。请参阅 this 博客 post 了解更多相关信息。

这两种方法都没有破坏 MVVM 模式。