我一直在努力以 MVVM 模式打開新視窗而不違反它的規則一個月。我已經閱讀了我認為這里的每一篇文章并觀看了關于此的每一個視頻,但作為一名業余程式員,我沒有找到簡單的解決方案來讓我理解。
最后,我根據找到的答案提出了解決方案。解決方案必須遵循一些條件:
- 依賴注入友好
- 不違反 MVVM 模式
- 可重復用于多個視圖
- 易于使用(無需為每個視窗輸入 100 行代碼)。
- 不允許嚴格的“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}" />
uj5u.com熱心網友回復:
在MVVM的背景關系中,對話框是View組件的一個模塊。按照MVVM的規則,View Model組件不允許處理控制元件或實作 UI 相關的邏輯。這意味著,視圖模型的一個類也不允許使用另一個處理控制元件或實作 UI 邏輯的類,因為這樣的類將是視圖組件的一部分。
控制元件必須始終在View組件中實體化和處理。
設計模式的定義要求模式必須獨立于任何語言、編譯器或平臺才能獲得資格。
由于代碼隱藏是純語言特性(因此是編譯器特性),代碼隱藏不能違反任何設計模式。
代碼隱藏(即partial類)是 WPF 的關鍵部分:并非所有內容都可以在 XAML 中實作。如果你不能在 XAML、依賴屬性或復雜邏輯中實作它,例如,它必須在View Model中的結論是非常錯誤的。
事實上,大多數與視圖相關的框架代碼都是用 C# 撰寫的。XAML 主要用于布局 UI:它直觀地反映了 UI 的樹形結構,并且在此背景關系中的可讀性方面優于 C#。此外,某些任務使用 XAML 更容易,例如創建一個DataTemplate。這只是在撰寫 UI 相關代碼時更喜歡XAML 而不是 C# 的原因。XAML 永遠無法替代 C#。
您的問題的解決方案是從代碼隱藏中顯示對話框,例如通過實作單擊處理程式。
為了增加靈活性,以下示例使用 aRoutedCommand來替換點擊處理程式。
這樣,對話框可以從任何定義相應控制元件的子控制元件CommandBinding(在本例中為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;
}
主視窗.xaml
<Window>
<local:ProductListView />
</Window>
產品串列視圖.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.
}
對話服務.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();
}
}
應用程式.xaml.cs
// Register the factory for the DialogService
services.AddSingleton<Func<IProductAddViewModel>>(serviceProvider => serviceProvider.GetService<IProductAddViewModel>);
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/489983.html
