问题正在从ServiceLocator
反模式转移到Dependency Injection
。鉴于我的经验不足,我无法将DI原则转换为现在实现的代码。
“摘要”部分是可选的,可供阅读。您可能想对某事发表评论,建议。
该程序的主要目的是合并特定信息的占位符字段的过程。信息数量众多,因此需要具有基础架构。例如表格,服务,数据库。我对此任务有一些经验。我设法基于WinForms
创建了一个类似的程序。而且它甚至有效!但是就模式,维护,可扩展性和性能而言,代码很糟糕。重要的是要了解编程是一种爱好。它不是主要的教育或工作。
在WinForms
上执行所描述任务的经验很糟糕。我开始研究模式和新平台。起点是UWP
和MVVM
。应该指出的是,绑定机制是惊人的。
途中的第一个问题是独立解决的。这与通过UWP
连接的NavigationView
中的ShellPage
在ShellViewModel
中导航有关。以及创建一个NavigationService
。它基于Windows Template Studio
中的模板。
由于存在有效的WinForms
程序及其反模式方向,所以有时间和渴望去做[[一切正确。
ServiceLocator
(或ViewModelLocator
)。我在Microsoft
的示例中找到了它,包括Windows Template Studio
的模板。这样,我便陷入了反模式陷阱。如上所述,我不想再这样。解决方案的第一件事就是dependency injection
。鉴于我的经验不足,我无法将DI原则转换为现在实现的代码。
当前实施
UWP
的起点是app.xaml.cs
。重点是将控制权转移到ActivationService
。它的任务是将Frame
添加到Window.Current.Content
并导航到默认页面-MainPage
。 Microsoft documentation。ViewModelLocator
是单例。对其属性的第一次调用将调用构造函数。
private static ViewModelLocator _current;
public static ViewModelLocator Current => _current ?? (_current = new ViewModelLocator());
// Constructor
private ViewModelLocator(){...}
将ViewModelLocator
与View
(Page
)一起使用,ShellPage
:
private ShellViewModel ViewModel => ViewModelLocator.Current.ShellViewModel;
ViewModelLocator
与ViewModel
的使用类似,ShellViewModel
:
private static NavigationService NavigationService => ViewModelLocator.Current.NavigationService;
移至DI
ShellViewModel
具有来自NavigationService
的ViewModelLocator
,如上所述。我现在如何去DI?实际上,程序很小。现在是摆脱反模式的好时机。代码
ViewModelLocator
private static ViewModelLocator _current; public static ViewModelLocator Current => _current ?? (_current = new ViewModelLocator()); private ViewModelLocator() { // Services SimpleIoc.Default.Register<NavigationService>(); // ViewModels and NavigationService items Register<ShellViewModel, ShellPage>(); Register<MainViewModel, MainPage>(); Register<SettingsViewModel, SettingsPage>(); } private void Register<TViewModel, TView>() where TViewModel : class where TView : Page { SimpleIoc.Default.Register<TViewModel>(); NavigationService.Register<TViewModel, TView>(); } public ShellViewModel ShellViewModel => SimpleIoc.Default.GetInstance<ShellViewModel>(); public MainViewModel MainViewModel => SimpleIoc.Default.GetInstance<MainViewModel>(); public SettingsViewModel SettingsViewModel => SimpleIoc.Default.GetInstance<SettingsViewModel>(); public NavigationService NavigationService => SimpleIoc.Default.GetInstance<NavigationService>();
ShellPage:页面
private ShellViewModel ViewModel => ViewModelLocator.Current.ShellViewModel; public ShellPage() { InitializeComponent(); // shellFrame and navigationView from XAML ViewModel.Initialize(shellFrame, navigationView); }
ShellViewModel:ViewModelBase
private bool _isBackEnabled; private NavigationView _navigationView; private NavigationViewItem _selected; private ICommand _itemInvokedCommand; public ICommand ItemInvokedCommand => _itemInvokedCommand ?? (_itemInvokedCommand = new RelayCommand<NavigationViewItemInvokedEventArgs>(OnItemInvoked)); private static NavigationService NavigationService => ViewModelLocator.Current.NavigationService; public bool IsBackEnabled { get => _isBackEnabled; set => Set(ref _isBackEnabled, value); } public NavigationViewItem Selected { get => _selected; set => Set(ref _selected, value); } public void Initialize(Frame frame, NavigationView navigationView) { _navigationView = navigationView; _navigationView.BackRequested += OnBackRequested; NavigationService.Frame = frame; NavigationService.Navigated += Frame_Navigated; NavigationService.NavigationFailed += Frame_NavigationFailed; } private void OnItemInvoked(NavigationViewItemInvokedEventArgs args) { if (args.IsSettingsInvoked) { NavigationService.Navigate(typeof(SettingsViewModel)); return; } var item = _navigationView.MenuItems.OfType<NavigationViewItem>().First(menuItem => (string)menuItem.Content == (string)args.InvokedItem); var pageKey = GetPageKey(item); NavigationService.Navigate(pageKey); } private void OnBackRequested(NavigationView sender, NavigationViewBackRequestedEventArgs args) { NavigationService.GoBack(); } private void Frame_Navigated(object sender, NavigationEventArgs e) { IsBackEnabled = NavigationService.CanGoBack; if (e.SourcePageType == typeof(SettingsPage)) { Selected = _navigationView.SettingsItem as NavigationViewItem; return; } Selected = _navigationView.MenuItems .OfType<NavigationViewItem>() .FirstOrDefault(menuItem => IsMenuItemForPageType(menuItem, e.SourcePageType)); } private void Frame_NavigationFailed(object sender, NavigationFailedEventArgs e) { throw e.Exception; } private bool IsMenuItemForPageType(NavigationViewItem item, Type sourcePageType) { var pageKey = GetPageKey(item); var navigatedPageKey = NavigationService.GetNameOfRegisteredPage(sourcePageType); return pageKey == navigatedPageKey; } private Type GetPageKey(NavigationViewItem item) => Type.GetType(item.Tag.ToString());
更新1
我对ServiceLocator
和ViewModelLocator
之间的相等性有误吗?称为ServiceLocator
(或ViewModelLocator
)基本上,当前任务是连接到
View
和ViewModel
。NavigationService
超出了此任务的范围。所以它不应该在ViewModelLocator
中吗?
public ShellViewModel(INavigationService navigation)
一旦完成,您就可以建立一个具有所有依赖关系的DI框架(如NInject)(有点像您的SimpleIoC之类的东西),并且,理想情况下,从容器中拉出一个根对象(它可以构造其他所有东西) 。通常,这是应用程序的主要视图模型。我已经在WPF和UWP的多个项目上成功做到了这一点,并且效果很好。您唯一需要注意的是在运行时创建视图模型时(通常这样做),方法是注入工厂。