【问题标题】:ViewModels references in ShellViewModel Caliburn.MicroShellViewModel Caliburn.Micro 中的 ViewModels 引用
【发布时间】:2016-10-19 07:54:13
【问题描述】:

在此线程中:Can anybody provide any simple working example of the Conductor<T>.Collection.AllActive usage? 我已经得到了部分答案,但我仍然感到困惑。

我只想将我的所有视图模型引用到我的 ShellViewModel 中,以便能够打开/关闭 ContentControls,但无需将它们全部注入构造函数中。

在回答中,建议在ShellViewModel的构造函数中注入接口。如果这样做,我是否必须将所有 ViewModel 注入实现该接口的类中?

public MyViewModel(IMagicViewModelFactory factory)
{
    FirstSubViewModel = factory.MagicallyGiveMeTheViewModelIWant();
    SecondSubViewModel = factory.MagicallyGiveMeTheViewModelIWant();
    ThirdSubViewModel = factory.MagicallyGiveMeTheViewModelIWant();

    Items.Add(FirstSubViewModel);
    Items.Add(SecondSubViewModel);
    Items.Add(ThirdSubViewModel);
}

另外,我想避免通过 IoC.Get 来获取我的视图模型的实例,如果我没记错的话,我认为这违反了 IoC 的原则。 在其他一些示例中,它们会在需要时创建新的视图模型,但在这种情况下使用 IoC 有什么意义,尤其是当我需要在这些新视图模型中注入服务时?

在我的 Shell 视图中,我有一个包含 3 个不同区域的布局,通过以下方式绑定到我的 Shell 视图模型:

 <ContentControl x:Name="Header"
                    Grid.ColumnSpan="3"/>
 <ContentControl x:Name="Menu"
                    Grid.Row="1"/>
 <ContentControl x:Name="Main"
                    Grid.ColumnSpan="3"/>

在我扩展 Conductor.Collection.AllActive 的 ShellViewModel 中,我引用了如下 3 个区域:

public Screen Menu { get; private set; }
public Screen Header { get; private set; }
public Screen Main { get; private set; }

我希望能够像这样更改它们:

Menu = Items.FirstOrDefault(x => x.DisplayName == "Menu");
Header = Items.FirstOrDefault(x => x.DisplayName == "Header");
Main = Items.FirstOrDefault(x => x.DisplayName == "Landing");

我所有的 ViewModel 都在其构造函数中设置了 DisplayName。

我试过了,但 GetChildren() 是空的

foreach (var screen in GetChildren())
        {
            Items.Add(screen);
        }

我是否遗漏了一些明显的东西?

提前致谢!

【问题讨论】:

    标签: mvvm dependency-injection caliburn.micro


    【解决方案1】:

    最后,我自己找到了答案。这一切都在 AppBootstrapper 中!

    我最终为我的屏幕创建了一个 ViewModelBase,以便它们都可以具有 IShell 属性(以便 ViewModel 可以触发 ShellViewModel 中的导航),如下所示:

    public class ViewModelBase : Screen
    {
        private IShell _shell;
    
        public IShell Shell
        {
            get { return _shell; }
            set
            {
                _shell = value;
                NotifyOfPropertyChange(() => Shell);
            }
        }
    }
    

    然后在 AppBoostrapper 中像这样注册它们:

    container.Singleton<ViewModelBase, SomeViewModel>();
    container.Singleton<ViewModelBase, AnotherViewModel>();
    container.Singleton<ViewModelBase, YetAnotherViewModel>();
    

    然后创建一个 IEnumerable 作为参数传递给我的 ShellViewModel ctor:

    IEnumerable<ViewModelBase> listScreens = GetAllScreenInstances();
    
    container.Instance<IShell>(new  ShellViewModel(listScreens));
    

    然后将 IShell 传递给每个 ViewModel

    foreach (ViewModelBase screen in listScreens)
    {
        screen.Shell = GetShellViewModelInstance();
    }
    

    为了完整起见,这里是我的 GetAllScreenInstances() 和 GetShellViewModelInstance() :

    protected IEnumerable<ViewModelBase> GetAllScreenInstances()
    {
         return container.GetAllInstances<ViewModelBase>();
    }
    protected IShell GetShellViewModelInstance()
    {
        var instance = container.GetInstance<IShell>();
        if (instance != null)
            return instance;
    
        throw new InvalidOperationException("Could not locate any instances.");
    }
    

    这是我的 ShellViewModel ctor 的样子:

    public ShellViewModel(IEnumerable<ViewModelBase> screens )
    {         
        Items.AddRange(screens);
    }
    

    希望这可以帮助将来的人!

    【讨论】:

    • 为什么不删除所有的枚举?您已经在 IoC 容器中注册了所有视图模型,因此您只需将它们添加为构造函数参数,它们将被隐式注入
    猜你喜欢
    • 1970-01-01
    • 2021-09-24
    • 1970-01-01
    • 1970-01-01
    • 2012-10-23
    • 2012-04-17
    • 2016-06-16
    • 2013-12-02
    • 1970-01-01
    相关资源
    最近更新 更多