在 WPF MVVM 模式中打开子窗口 - 正确的解决方案?

问题描述 投票:0回答:2

一个月以来,我一直在努力以 MVVM 模式打开新窗口而不违反其规则。我已经阅读了我认为这里的每一篇文章并观看了关于此的每个视频,但作为一名业余程序员,我没有找到简单的解决方案来理解。

最后我根据我找到的答案想出了解决方案。 解决方案必须遵循一些条件:

  1. 依赖注入友好
  2. 不违反MVVM模式
  3. 可重复用于多个视图
  4. 易于使用(无需为每个窗口键入 100 行代码)。
  5. 不允许使用严格的“DialogMVVM”等库。 (我正处于学习阶段,所以我想了解我的代码在做什么。)

免责声明:我不需要从对话框中获取结果,因此它不包含在此处。

请告诉我我的解决方案是否正确。

1。我用DataTemplate制作了DialogWindow模板:

<Window x:Class="AWH.Views.DialogWindow"
        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:viewmodels="clr-namespace:AWH.ViewModels" 
        xmlns:views="clr-namespace:AWH.Views"
        mc:Ignorable="d"
        SizeToContent="WidthAndHeight">
    <Window.Resources>
        <DataTemplate DataType="{x:Type viewmodels:ProductAddViewModel}">
            <views:ProductView/>
        </DataTemplate>
    </Window.Resources>
</Window>

2。 IDialogService 以及 DialogService 中的实现

public interface IDialogService
{
    void ShowDialog(object viewModel);
}

public class DialogService : IDialogService
{
    public void ShowDialog(object viewModel)
    {
        var win = new DialogWindow();
        win.Content = viewModel;
        win.ShowDialog();
    }
}

3.从 ViewModel 打开窗口(在本例中为 ProductListViewModel)

public class ProductListViewModel : ViewModelBase
{
        private IDialogService _dialogService;
        private IProductAddViewModel _productAddViewModel;
        public ICommand AddProductCommand { get; set; }

        public ProductListViewModel(IDialogService dialogService, IProductAddViewModel productAddViewModel)
        {
            _productAddViewModel = productAddViewModel;
            _dialogService = dialogService;
    
            AddProductCommand = new DelegateCommand(OpenAddProductDialog);
        }

        private void OpenAddProductDialog()
        {
        _dialogService.ShowDialog(_productAddViewModel);
        }
}

4。在 App.xaml.cs 中注入依赖项(我正在使用 IServiceCollection)

        services.AddSingleton<ProductListViewModel>();
        services.AddSingleton<IProductAddViewModel, ProductAddViewModel>();
        services.AddSingleton<IDialogService, DialogService>();

就是这样。如果我的想法正确的话,我并没有违反 MVVM 模式,因为 viewmodel 没有调用 view,而是调用 viewmodel,而 WPF 通过 DataTemplates 完成剩下的工作。

我说得对吗?

编辑:当然,您需要一种从其他视图打开此窗口的方法。所以(在本例中)ProductListView.xaml(这是对应于 ProductListViewModel 的视图):

    <Button Content="Add product" Margin="10 15" Padding="8 5" VerticalAlignment="Stretch" Command="{Binding AddProductCommand}" />
c# wpf mvvm dependency-injection modal-dialog
2个回答
2
投票

MVVM 的上下文中,对话框是 View 组件的模块。遵循MVVM的规则,View Model组件不允许处理控件或实现UI相关逻辑。这意味着,View Model 的类也不允许使用另一个处理控件或实现 UI 逻辑的类,因为这样的类将是 View 组件的一部分。

控件必须始终在 View 组件中实例化和处理。

设计模式的定义要求模式必须独立于任何语言、编译器或平台才能符合资格。
由于代码隐藏是纯粹的语言功能(因此也是编译器功能),因此代码隐藏不能违反任何设计模式。

代码隐藏,即

partial
类,是 WPF 的关键部分:并非所有内容都可以在 XAML 中实现。如果您无法在 XAML、依赖属性或一般复杂逻辑中实现它,那么它必须在 View Model 中实现,这是非常错误的。
事实上,大多数与视图相关的框架代码都是用 C# 编写的。 XAML 主要用于布局 UI:它直观地反映了 UI 的树结构,并且在该上下文中的可读性方面优于 C#。此外,某些任务使用 XAML 会更容易,例如创建
DataTemplate
。这只是在编写 UI 相关代码时更喜欢XAML 而不是 C# 的原因。 XAML 永远无法取代 C#。

问题的解决方案是从代码隐藏中显示对话框,例如通过实现点击处理程序。

为了增加更多灵活性,以下示例使用

RoutedCommand
来替换单击处理程序。
这样,可以从任何定义相应
CommandBinding
的控件的子控件(在本例中为
MainWindow
)中显示对话框:

MainWindow.xaml.cs

public partial class MainWindow : Window
{
  public static RoutedUICommand ShowAddProductDialogCommand { get; } = new RoutedUICommand(
    "Show the product dialog", 
    nameof(ShowAddProductDialogCommand), 
    typeof(MainWindow));

  private IDialogService DialogService { get; }

  public MainWindow(IDialogService dialogService)
  {
    InitializeComponent();

    this.DialogService = dialogService;
    var dialogCommandBinding = new CommandBinding(ShowDialogCommand, ExecuteShowDialogCommand, CanExecuteShowDialogCommand);
    this.CommandBindings.Add(dialogCommandBinding);
  }

  private void ExecuteShowDialogCommand(object sender, ExecutedRoutedEventArgs e) 
    => this.DialogService.ShowAddProductDialog();

  private void CanExecuteShowDialogCommand(object sender, CanExecuteRoutedEventArgs e) 
    => e.CanExecute = true;
}

MainWindow.xaml

<Window>
  <local:ProductListView />
</Window>

ProductListView.xaml

<UserControl>
  <Button Content="Show Dialog"
          Command="{x:Static local:MainWindow.ShowAddProductDialogCommand}" />
</UserControl>

IDialogService.cs

public interface IDialogService
{
  void ShowAddProductDialog();

  // Add more methods - one for each dialog.
  // Each method knows how to configure and show the dialog window.
}

DialogService.cs

public class DialogService : IDialogService
{
  private Func<IProductAddViewModel> AddProductDialogViewModelFactory { get; }

  // Inject a factory for each dialog/ContentTemplate to create new view models for each dialog.
  // Add more factories - one for each dialog/dialog view model.
  public DialogService(Func<IProductAddViewModel> addProductDialogViewModelFactory)
  {
    this.AddProductDialogViewModelFactory = addProductDialogViewModelFactory;
  }

  public void ShowAddProductDialog()
  {
    IProductAddViewModel dialogDataContext = this.AddProductDialogViewModelFactory.Invoke();
    var dialog = new DialogWindow() 
    {    
      DataContext = dialogDataContext,
      Content = dialogDataContext
    };
    
    dialog.ShowDialog();
  }
}

App.xaml.cs

// Register the factory for the DialogService
services.AddSingleton<Func<IProductAddViewModel>>(serviceProvider => serviceProvider.GetService<IProductAddViewModel>);

0
投票

您可以在Brian Lagunas的这个示例中以非常有效的方式使用棱镜。

© www.soinside.com 2019 - 2024. All rights reserved.