RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 1080537
Accepted
EvgeniyZ
EvgeniyZ
Asked:2020-02-10 06:00:20 +0000 UTC2020-02-10 06:00:20 +0000 UTC 2020-02-10 06:00:20 +0000 UTC

根据 MVVM 规则在 WPF 中的 IoC

  • 772

渐渐地,我开始研究 IoC 和所有这些美食,现在我只是想不通如何根据 MVVM 的规则在 WPF 应用程序中使用它们。

假设我做了一些容器设置(我使用 Autofac):

class ContainerConfig
{
    public static IContainer Configure()
    {
        ISettings settings = new ConfigurationBuilder<ISettings>().UseJsonFile("Settings.json").Build();
        var builder = new ContainerBuilder();

        builder.RegisterType<MainViewModel>().SingleInstance();
        builder.RegisterInstance(settings).SingleInstance();

        return builder.Build();
    }
}

到目前为止,我在其中注册了 2 个对象:

  1. MainViewModel- 应用程序的主虚拟机,据我了解,它应该在一个实例中。
  2. 一些应用程序设置对象。

接下来,我覆盖OnStartup,为了创建一个窗口,设置它DataContext并显示所有这些:

public partial class App : Application
{
    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);
        var container = ContainerConfig.Configure();
        using var scope = container.BeginLifetimeScope();
        var mainViewModel = scope.Resolve<MainViewModel>();
        new MainWindow() { DataContext = mainViewModel }.Show();
    }
}

看来,当我朝着正确的方向前进时,或者不是?
好的,接下来要测试工​​作,我将通过写入MainViewModel以下内容来简单地绑定设置中的属性:

class MainViewModel
{
    public ISettings Settings { get; }

    public MainViewModel(ISettings settingsModel)
    {
        Settings = settingsModel;
    }
}

以及绑定本身:

<TextBlock Text="{Binding Settings.SomeValue}"/>

一切似乎都正常,一切都很好。
现在假设我需要创建另一个 VM,例如,它需要一个主 VM,我这样做:

class SecondViewModel
{
    private MainViewModel main;
    public SecondViewModel(MainViewModel mainViewModel)
    {
        main = mainViewModel;
    }

    public int Test { get; set; } = 33;

    private void SomeMethod()
    {
        main.SomeProperty = false;
    }
}

我注册它:

builder.RegisterType<SecondViewModel>().SingleInstance();

好吧,我添加到MainViewModel新的虚拟机:

class MainViewModel
{
    public ISettings Settings { get; }
    public SecondViewModel Second { get; }

    public MainViewModel(ISettings settingsModel, SecondViewModel second)
    {
        Settings = settingsModel;
        Second = second;
    }
}

结果,我得到一个循环错误,然后我清楚地明白我做错了什么。
在搜索了一些信息后,我找到了一种解决方法。

简而言之,正如您所看到的,我不太了解这一切应该如何工作,并且出现了很多问题,例如:

  1. 我做对了吗?
  2. 容器中应该记录什么?
  3. VM 层是否需要接口?
  4. 如何不破坏 MVVM?

一般来说,帮助我弄清楚如何在 WPF 应用程序中正确实现 IoC,甚至使用 MVVM?

c#
  • 1 1 个回答
  • 10 Views

1 个回答

  • Voted
  1. Best Answer
    tym32167
    2020-02-10T07:14:35Z2020-02-10T07:14:35Z

    DI 容器是一种工具,可以为您完成您可以在不违反任何原则的情况下自己完成的工作。

    如您所知,容器是一种为您创建对象并跟踪其生命周期的东西。

    在您的代码中,您在构造函数中创建了 2 个具有循环依赖关系的类,这使得无法创建此类类(除非将 NULL 传递给构造函数)。尝试手动创建类,你会发现你会失败,因为创建类A需要提供类B,而创建类B需要提供类A。这个循环称为循环依赖,并非如此很容易克服它。

    但是,正如我所说,克服它并不容易,但是有可能,例如,如果您为类定义一个工厂并将工厂传递给构造函数。然后,只有当您不尝试直接在构造函数内部访问工厂时,它才会起作用。

    例如

    class A
    {
        IBFactory _bfactory;
    
        public A(IBFactory bfactory)
        {
            _bfactory = bfactory;
        }
    
        void Foo()
        {
            var b = _bfactory.GetB();
            // do stuff
        }
    }
    
    class B
    {
        A _a;
        public B(A a)
        {
            _a = a;
        }
    }
    
    interface IBFactory{
        B GetB();
    }
    

    IBFactory 实现可能是最简单的

    class BFactory : IBFactory
    {
        IContainer _container;
    
        public BFactory(IContainer container)
        {
            _container = container;
        }
    
        public B GetB(){
            return _container.Resolve<B>();
        }
    }
    

    如果我没记错的话,一些容器本身就支持创建这样的工厂。在这种情况下,懒惰的程序员,而不是工厂,只是将整个容器转发给 A 类,这很丑陋并且是一种反模式 - 服务定位器。

    但是,我还想说,理想情况下,从属模型想要了解主要模型的这种情况不应该存在。努力设计尽可能自给自足的虚拟机。如果虚拟机之间必须进行通信,那么在一般情况下有一个消息总线,或者您可以转发您的一些接口,但通常这种通信应该受到限制。虚拟机之间的对话越少,调试的麻烦就越少。

    现在让我们来回答问题:

    我做对了吗?

    我们的职业没有什么是对的,但我会以不同的方式解决你的问题。

    class SecondViewModel
    {    
        public SecondViewModel()
        {        
        }
    
        event EventHandler<MyEventArgs> SomethingHappened;
        private void OnSomethingHappened(...) =>  SomethingHappened?.Invoke(....);
    
        ///......    
    
        private void SomeMethod()
        {
            OnSomethingHappened(...);      
        }
    }
    
    class MainViewModel
    {
        public ISettings Settings { get; }
        public SecondViewModel Second { get; }
    
        public MainViewModel(ISettings settingsModel, SecondViewModel second)
        {
            Settings = settingsModel;
            Second = second;
            Second.SomethingHappened += Second_SomethingHappened;
        }
    
        private void Second_SomethingHappened(object sender, MyEventArgs args)
        {
            this.SomeProperty = false;
        }
    }
    

    因此,第二个模型不知道任何主模型,并且主模型对从属模型的反应逻辑存储在一个地方 - 在主模型中。

    容器中应该记录什么?

    无论您打算使用什么。您的问题不在容器中,而是在您的类的组织中 - 在循环依赖中。

    VM 层是否需要接口?

    取决于你的班级组织。有时它是有道理的,有时它没有(更多时候它没有)。在您的情况下,接口不会帮助您。

    如何不破坏 MVVM?

    您在这里仅使用 VM 层进行操作,因为无论您如何制作该层,它本身都不会违反 MVVM。

    • 3

相关问题

  • 如何知道类中的方法是否属于接口?

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    如何从列表中打印最大元素(str 类型)的长度?

    • 2 个回答
  • Marko Smith

    如何在 PyQT5 中清除 QFrame 的内容

    • 1 个回答
  • Marko Smith

    如何将具有特定字符的字符串拆分为两个不同的列表?

    • 2 个回答
  • Marko Smith

    导航栏活动元素

    • 1 个回答
  • Marko Smith

    是否可以将文本放入数组中?[关闭]

    • 1 个回答
  • Marko Smith

    如何一次用多个分隔符拆分字符串?

    • 1 个回答
  • Marko Smith

    如何通过 ClassPath 创建 InputStream?

    • 2 个回答
  • Marko Smith

    在一个查询中连接多个表

    • 1 个回答
  • Marko Smith

    对列表列表中的所有值求和

    • 3 个回答
  • Marko Smith

    如何对齐 string.Format 中的列?

    • 1 个回答
  • Martin Hope
    Alexandr_TT 2020年新年大赛! 2020-12-20 18:20:21 +0000 UTC
  • Martin Hope
    Alexandr_TT 圣诞树动画 2020-12-23 00:38:08 +0000 UTC
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Qwertiy 号码显示 9223372036854775807 2020-07-11 18:16:49 +0000 UTC
  • Martin Hope
    user216109 如何为黑客设下陷阱,或充分击退攻击? 2020-05-10 02:22:52 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5