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



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

根据我找到的答案,我终于想出了解决办法。解决方案必须遵循一些条件:
  1. 依赖注入友好
  2. 不违反MVVM模式
  3. 可用于多个视图
  4. 易于使用(无需为每个窗口输入100行代码)。
  5. 没有严格的"等图书馆允许。(我在学习阶段,所以我想了解我的代码在做什么。)

免责声明:我不需要从对话框中获得结果,所以这里不包括

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

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。DialogService及其在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 . example .cs中注入依赖(我使用IServiceCollection)

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

这它。如果我认为正确,我没有违反MVVM模式,因为视图模型不调用视图,它调用视图模型和WPF正在通过DataTemplates做其余的。

我说的对吗?

编辑:当然你需要一种从其他视图打开这个窗口的方法。所以(在这个例子中)ProductListView。xaml(这是一个与ProductListViewModel相对应的视图):
<Button Content="Add product" Margin="10 15" Padding="8 5" VerticalAlignment="Stretch" Command="{Binding AddProductCommand}" />

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

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

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

代码隐藏,即partial类,是WPF的关键部分:不是所有的东西都可以在XAML中实现。如果你不能在XAML、依赖属性或复杂逻辑中实现它,那么它必须在视图模型中实现,这是非常错误的。
事实上,大多数与视图相关的框架代码都是用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>);

相关内容

  • 没有找到相关文章

最新更新