如何在 MVVM - WPF 应用程序中的 UserControl 之间进行通信

How to communicate between UserControl in MVVM - WPF Application

我想创建一个应用程序,其左侧的菜单会更改右侧的内容。 为此,我有一个带有两个 ContentControl 的 MainWindow(一个将包含一个 UserControl 'Menu',另一个将包含所选的 UserControl 'Red' 或 'Green'。

问题是右边的内容没有改变...

我做了一些研究,看到了依赖注入、委托、事件、消息总线、ViewModelLocator 等概念...... 但我不知道在这种情况下哪个最合适以及如何实施。 (我不想使用 MVVMLight 或任何类似的插件)

提前感谢您的关注。

为此,我使用了 MVVM 模式和 DataTemplate 绑定:

App.xaml

<Application.Resources>
    <DataTemplate DataType="{x:Type viewModel:MainViewModel}">
        <view:MainWindow />
    </DataTemplate>
    <DataTemplate DataType="{x:Type viewModelMenu:LeftViewModel}">
        <viewMenu:Left />
    </DataTemplate>
    <DataTemplate DataType="{x:Type viewModelContent:RedViewModel}">
        <viewContent:Red />
    </DataTemplate>
</Application.Resources>

ViewModel.cs

public abstract class ViewModel : INotifyPropertyChanged
{
    #region Properties

    private ViewModel _mainContent;

    public ViewModel MainContent
    {
        get => _mainContent;
        set
        {
            _mainContent = value;
            OnPropertyChanged();
            MessageBox.Show(nameof(MainContent));
        }
    }

    #endregion Properties

    public ViewModel()
    {
        InitCommands();
    }

    protected abstract void InitCommands();

    #region Factory Method - CreateCommand

    protected ICommand CreateCommand(Action execute, Func<bool> canExecute)
    {
        return new RelayCommand(
            execute: execute,
            canExecute: canExecute
            );
    }

    protected ICommand CreateCommand<T>(Action<T> execute, Predicate<T> canExecute)
    {
        return new RelayCommand<T>(
            execute: execute,
            canExecute: canExecute
            );
    }

    #endregion Factory Method - CreateCommand

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = "")
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

主要ViewModel.cs

internal class MainViewModel : ViewModel
{
    private ViewModel _leftMenu;
    public ViewModel LeftMenu
    {
        get => _leftMenu;
        set
        {
            _leftMenu = value;
            OnPropertyChanged();
        }
    }

    public MainViewModel()
    {
        LeftMenu = new LeftViewModel();
    }

    protected override void InitCommands()
    {
    }

左ViewModel.cs

internal class LeftViewModel : ViewModel
{
    public ICommand ChangeContentToRed { get; set; }
    public ICommand ChangeContentToGreen { get; set; }

    protected override void InitCommands()
    {
        ChangeContentToRed = new RelayCommand(
            execute: () => MainContent = new RedViewModel(),
            canExecute: () => !(MainContent is RedViewModel)
            );

        ChangeContentToGreen = new RelayCommand(
            execute: () => MainContent = new GreenViewModel(),
            canExecute: () => !(MainContent is GreenViewModel)
            );
    }
}

RedViewModel 和 GreenViewModel 是空的,所以我没有显示代码,而是扩展了 ViewModel

Window.xaml

 <Window.DataContext>
    <viewModel:MainViewModel />
</Window.DataContext>

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*" />
        <ColumnDefinition Width="3*" />
    </Grid.ColumnDefinitions>

    <ContentControl Grid.Column="0" Content="{Binding Path=LeftMenu}" />
    <ContentControl Grid.Column="1" Content="{Binding Path=MainContent}" />
</Grid>

Left.xaml

<UserControl.DataContext>
    <viewModel:LeftViewModel />
</UserControl.DataContext>

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="*" />
        <RowDefinition Height="*" />
    </Grid.RowDefinitions>
    <Button
        Grid.Row="0"
        Command="{Binding Path=ChangeContentToRed}"
        Content="Red" />
    <Button
        Grid.Row="1"
        Command="{Binding Path=ChangeContentToGreen}"
        Content="Green" />
</Grid>

Red 和 Green 只是两个 UserControl,有一个红色和一个绿色网格

当你有像

这样的数据模板时
<DataTemplate DataType="{x:Type viewModelMenu:LeftViewModel}">
    <viewMenu:Left />
</DataTemplate>

然后将 LeftViewModel 类型的值分配给 ContentControl 的内容 属性,例如

<ContentControl Content="{Binding Path=LeftMenu}"/>

DataTemplate赋值给了ContentControl的ContentTemplate,实例化的DataTemplate中的元素(即你的UserControl)继承了ContentControl的ControlTemplate中ContentPresenter的DataContext,然后持有Content值。

但是,这仅在您未显式分配 UserControl 的 DataContext 并因此破坏 DataContext 的值继承时有效 属性。

您必须从 UserControl 中删除显式 DataContext 分配,即:

<UserControl.DataContext>
    <viewModel:LeftViewModel />
</UserControl.DataContext>