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



我构建了一个虚拟UserControl,其代码隐藏中有一个显示消息的方法。我在主窗口中使用了这个控件,并希望在使用命令和 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>

public partial class Control1 : UserControl
{
public Control1()
{
InitializeComponent();
}
public void ShowMessage()
{
MessageBox.Show("Called from other control!");
}
}
<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();
}
public event Action ShowMessage;
private ICommand m_showMessageCommand;
public ICommand ShowMessageCommand
{
get
{
return m_showMessageCommand ?? (m_showMessageCommand = new RelayCommand(
p => true,
p => ShowMessage?.Invoke()));
}
}

如果只需要显示消息,则应将ShowMessage()方法移动到视图模型,并使用消息服务从视图模型类执行此操作。

如果您确实想调用一些仅在视图中定义才有意义的方法,则可以通过在视图中实现接口并使用此接口注入视图模型来完成此操作。例如,当您调用命令时:

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以外的其他东西。

另一种选择是使用事件聚合器或信使以耦合的方式将事件或消息从视图模型发送到视图。有关此内容的更多信息,请参阅此博客文章。

这两种方法都不会破坏 MVVM 模式。

最新更新