我最终决定学习如何在 MVVM 框架内使应用程序界面可扩展为多个 View / ViewModel。我遇到了Inversion of Control + Depency Injection 模板,上面有很多信息,很多人建议用它来组织应用程序的逻辑架构。此外,它还提供了组织诸如单例存储等便利的机会,这正是我所需要的。
问题是,在作者推荐的文章中,这就是全部,几乎没有人在 WPF + MVVM 中“为傻瓜”手动显示 IoC 实现。
如果不了解它是如何工作的,我就不能在我的项目中使用“黑匣子”,我通常不喜欢大的、可怕的和无法理解的东西,不清楚它是如何工作的,我总是试图挖掘这个非常本质。我决定对一项实际任务进行分析 - 制作带有ListBox
页面选择的多页面界面。
从两个接口开始
public interface IView
{
string Title { get; set; }
object DataContext { get; set; }
}
public interface IViewModel
{
}
然后把他们都搞砸了
public sealed partial class MainWindow : Window, IView {...}
public partial class Page1 : Page, IView {...}
public partial class Page2 : Page, IView {...}
public class MainViewModel : NotifyPropertyChanged, IViewModel {...}
public class Page1ViewModel : NotifyPropertyChanged, IViewModel {...}
public class Page2ViewModel : NotifyPropertyChanged, IViewModel {...}
我用单例实现了这两个容器
public static class ViewService
{
private static readonly ConcurrentDictionary<Type, IView> instances = new ConcurrentDictionary<Type, IView>();
public static T GetView<T>(IViewModel viewModel = null) where T : IView
{
if (!instances.TryGetValue(typeof(T), out IView view))
{
view = (T)Activator.CreateInstance(typeof(T));
instances[typeof(T)] = view;
}
if (viewModel != null)
view.DataContext = viewModel;
return (T)view;
}
}
public static class ViewModelService
{
private static readonly ConcurrentDictionary<Type, IViewModel> instances = new ConcurrentDictionary<Type, IViewModel>();
public static T GetViewModel<T>() where T : IViewModel
{
if (!instances.TryGetValue(typeof(T), out IViewModel viewModel))
{
viewModel = (T)Activator.CreateInstance(typeof(T));
instances[typeof(T)] = viewModel;
}
return (T)viewModel;
}
}
我像这样创建主窗口
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
MainViewModel vm = ViewModelService.GetViewModel<MainViewModel>();
MainWindow window = ViewService.GetView<MainWindow>(vm);
window.Show();
}
}
并且页面在主视图模型的构造函数中
public class MainViewModel : NotifyPropertyChanged, IViewModel
{
private IView _currentPage;
public List<IView> Pages { get; }
public IView CurrentPage
{
get => _currentPage;
set
{
_currentPage = value;
OnPropertyChanged();
OnPropertyChanged(nameof(Title));
}
}
public string Title => $"IoC Demo - {CurrentPage.Title}";
public MainViewModel()
{
Pages = new List<IView>
{
ViewService.GetView<Page1>(ViewModelService.GetViewModel<Page1ViewModel>()),
ViewService.GetView<Page2>(ViewModelService.GetViewModel<Page2ViewModel>())
};
CurrentPage = Pages[0];
}
}
主窗口MainWindow
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<ListBox BorderThickness="0" Background="AliceBlue" ItemsSource="{Binding Pages}" SelectedItem="{Binding CurrentPage}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Title}" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="5"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Frame Content="{Binding CurrentPage}" Grid.Column="1" NavigationUIVisibility="Hidden"/>
</Grid>
页面是一样的,只有一种类型Page1
,第二种Page2
<Grid>
<TextBlock Margin="5" Text="{Binding Text}"/>
</Grid>
页面视图模型(用于可见性)
public class Page1ViewModel : NotifyPropertyChanged, IViewModel
{
public string Text { get; } = "Page one";
}
这一切都可以协同工作。
问题:我是否违反了 MVVM,我是否正确地使用单例实现了 IoC 容器?
附言
- WPF 中的 IoC 根据来自 @EvgeniyZ 和 @tym32167 的 MVVM 规则我已经阅读过。这很有帮助,谢谢!
它似乎已经掌握了它。
DataContext
.接口
IViewModel
被丢弃为无用实现了一个IoC容器,这次希望是真的
OnStartup
现在如下所示:MainViewModel
是这样的:感谢所有评论者,他们为我提供了非常必要的信息,尤其是SimpleIoC 提供了帮助。
在 Yandex.Disk链接上使用此解决方案进行PS存档。