如何在 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>
我想创建一个应用程序,其左侧的菜单会更改右侧的内容。 为此,我有一个带有两个 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>