Caliburn.Micro嵌套ViewModels最佳实践

鲁尔波特爱国者

这是一个很长的问题,请耐心等待。

目前,我正在开发一个小工具,旨在帮助我跟踪“故事”中的众多角色。

该工具执行以下操作:

  • 将当前存储为json的字符加载到磁盘上,并将它们存储在列表中,该列表通过列表框在Shell中显示。
  • 如果用户随后打开一个字符,则Shell(即a Conductor<Screen>.Collection.OneActive)将打开一个新的字符(CharacterViewModel源自)Screen
  • Character获取即将通过要打开的汉字IEventAggregator信息系统。
  • CharacterViewModel还具有其是子的ViewModels结合到各种子视图的各种属性。

这是我的问题:当前,我在初始化时手动初始化子ViewModels ChracterViewModel但这对我来说听起来像是可疑的,我很确定有更好的方法可以做到这一点,但是我不知道应该怎么做。

这是的代码CharacterViewModel

/// <summary>ViewModel for the character view.</summary>
public class CharacterViewModel : Screen, IHandle<DataMessage<ICharacterTagsService>>
{
    // --------------------------------------------------------------------------------------------------------------------
    // Fields
    // -------------------------------------------------------------------------------------------------------------------

    /// <summary>The event aggregator.</summary>
    private readonly IEventAggregator eventAggregator;

    /// <summary>The character tags service.</summary>
    private ICharacterTagsService characterTagsService;

    // --------------------------------------------------------------------------------------------------------------------
    // Constructors & Destructors
    // -------------------------------------------------------------------------------------------------------------------

    /// <summary>Initializes a new instance of the <see cref="CharacterViewModel"/> class.</summary>
    public CharacterViewModel()
    {
        if (Execute.InDesignMode)
        {
            this.CharacterGeneralViewModel = new CharacterGeneralViewModel();

            this.CharacterMetadataViewModel = new CharacterMetadataViewModel();
        }
    }

    /// <summary>Initializes a new instance of the <see cref="CharacterViewModel"/> class.</summary>
    /// <param name="eventAggregator">The event aggregator.</param>
    [ImportingConstructor]
    public CharacterViewModel(IEventAggregator eventAggregator)
        : this()
    {
        this.eventAggregator = eventAggregator;
        this.eventAggregator.Subscribe(this);
    }

    // --------------------------------------------------------------------------------------------------------------------
    // Properties
    // -------------------------------------------------------------------------------------------------------------------

    /// <summary>Gets or sets the character.</summary>
    public Character Character { get; set; }

    /// <summary>Gets or sets the character general view model.</summary>
    public CharacterGeneralViewModel CharacterGeneralViewModel { get; set; }

    /// <summary>Gets or sets the character metadata view model.</summary>
    public CharacterMetadataViewModel CharacterMetadataViewModel { get; set; }

    /// <summary>Gets or sets the character characteristics view model.</summary>
    public CharacterApperanceViewModel CharacterCharacteristicsViewModel { get; set; }

    /// <summary>Gets or sets the character family view model.</summary>
    public CharacterFamilyViewModel CharacterFamilyViewModel { get; set; }

    // --------------------------------------------------------------------------------------------------------------------
    // Methods
    // -------------------------------------------------------------------------------------------------------------------

    /// <summary>Saves a character to the file system as a json file.</summary>
    public void SaveCharacter()
    {
        ICharacterSaveService saveService = new JsonCharacterSaveService(Constants.CharacterSavePathMyDocuments);

        saveService.SaveCharacter(this.Character);

        this.characterTagsService.AddTags(this.Character.Metadata.Tags);
        this.characterTagsService.SaveTags();
    }

    /// <summary>Called when initializing.</summary>
    protected override void OnInitialize()
    {
        this.CharacterGeneralViewModel = new CharacterGeneralViewModel(this.eventAggregator);
        this.CharacterMetadataViewModel = new CharacterMetadataViewModel(this.eventAggregator, this.Character);
        this.CharacterCharacteristicsViewModel = new CharacterApperanceViewModel(this.eventAggregator, this.Character);
        this.CharacterFamilyViewModel = new CharacterFamilyViewModel(this.eventAggregator);

        this.eventAggregator.PublishOnUIThread(new CharacterMessage
        {
            Data = this.Character
        });


        base.OnInitialize();
    }

    /// <summary>
    /// Handles the message.
    /// </summary>
    /// <param name="message">The message.</param>
    public void Handle(DataMessage<ICharacterTagsService> message)
    {
        this.characterTagsService = message.Data;
    }
}

对于清酒,我还为您提供了一个子ViewModels。其他的一个并不重要,因为它们的结构相同,只是执行不同的任务。

/// <summary>The character metadata view model.</summary>
public class CharacterMetadataViewModel : Screen
{
    /// <summary>The event aggregator.</summary>
    private readonly IEventAggregator eventAggregator;

    /// <summary>Initializes a new instance of the <see cref="CharacterMetadataViewModel"/> class.</summary>
    public CharacterMetadataViewModel()
    {
        if (Execute.InDesignMode)
        {
            this.Character = DesignData.LoadSampleCharacter();
        }
    }

    /// <summary>Initializes a new instance of the <see cref="CharacterMetadataViewModel"/> class.</summary>
    /// <param name="eventAggregator">The event aggregator.</param>
    /// <param name="character">The character.</param>
    public CharacterMetadataViewModel(IEventAggregator eventAggregator, Character character)
    {
        this.Character = character;

        this.eventAggregator = eventAggregator;
        this.eventAggregator.Subscribe(this);
    }

    /// <summary>Gets or sets the character.</summary>
    public Character Character { get; set; }

    /// <summary>
    /// Gets or sets the characters tags.
    /// </summary>
    public string Tags
    {
        get
        {
            return string.Join("; ", this.Character.Metadata.Tags);
        }

        set
        {
            char[] delimiters = { ',', ';', ' ' };

            List<string> tags = value.Split(delimiters, StringSplitOptions.RemoveEmptyEntries).ToList();

            this.Character.Metadata.Tags = tags;
            this.NotifyOfPropertyChange(() => this.Tags);
        }
    }
}

我已经读过《屏幕,导体和成分》,《IResult》和《协程》并浏览了其余的文档,但是以某种方式我找不到我想要的东西。

// edit:我应该提到我的代码可以正常工作。我只是对它不满意,因为我认为我不太了解MVVM的概念,因此编写了错误的代码。

Teagans爸爸

一个ViewModel实例化多个子ViewModel并没有错。如果您要构建更大或更复杂的应用程序,则要保持代码的可读性和可维护性,这几乎是不可避免的。

在您的示例中,每当创建的实例时,您都将实例化所有四个子ViewModel CharacterViewModel每个子ViewModels都IEventAggregator作为依赖项。我建议您将这四个子ViewModel作为主要对象的依赖项,CharacterViewModel并通过构造函数将其导入:

[ImportingConstructor]
public CharacterViewModel(IEventAggregator eventAggregator,
                            CharacterGeneralViewModel generalViewModel,
                            CharacterMetadataViewModel metadataViewModel,
                            CharacterAppearanceViewModel appearanceViewModel,
                            CharacterFamilyViewModel familyViewModel)
{
    this.eventAggregator = eventAggregator;
    this.CharacterGeneralViewModel generalViewModel;
    this.CharacterMetadataViewModel = metadataViewModel;
    this.CharacterCharacteristicsViewModel = apperanceViewModel;
    this.CharacterFamilyViewModel = familyViewModel;

    this.eventAggregator.Subscribe(this);
}

因此,您可以将子ViewModel属性上的设置器设为私有。

更改您的子ViewModels以IEventAggregator通过构造函数注入导入

[ImportingConstructor]
public CharacterGeneralViewModel(IEventAggregator eventAggregator)
{
    this.eventAggregator = eventAggregator;
}

在您的示例中,这些子ViewModel中的两个Character在其构造函数中传递了一个数据实例,这意味着一个依赖关系。在这些情况下,我将为每个子ViewModel提供一个公共Initialize()方法,您可以在其中设置Character数据并在那里激活事件聚合器订阅:

public Initialize(Character character)
{
    this.Character = character;
    this.eventAggregator.Subscribe(this);   
}

然后在您的CharacterViewModel OnInitialize()方法中调用此方法:

protected override void OnInitialize()
{    
    this.CharacterMetadataViewModel.Initialize(this.Character);
    this.CharacterCharacteristicsViewModel.Initialize(this.Character);    

    this.eventAggregator.PublishOnUIThread(new CharacterMessage
    {
        Data = this.Character
    });


    base.OnInitialize();
}

对于仅Character通过来更新数据的子ViewModels EventAggregator,将this.eventAggregator.Subscribe(this)调用保留在构造函数中。

如果页面正常运行实际上不需要您的任何子ViewModel,则可以通过属性导入来初始化这些VM属性:

[Import]
public CharacterGeneralViewModel CharacterGeneralViewModel { get; set; }

直到构造函数完成运行后,才进行属性导入。

我还建议也ICharacterSaveService通过构造函数注入处理实例化,而不是每次保存数据时都显式创建一个新实例。

MVVM的主要目的是允许前端设计人员使用可视化工具(Expression Blend)来处理UI的布局,并允许编码人员在不相互干扰的情况下实现行为和业务。ViewModel公开要绑定到视图的数据,以抽象级别描述视图的行为,并经常充当后端服务的中介。

没有一种“正确”的方法可以做到这一点,并且在某些情况下它不是最佳解决方案。有时候,最好的解决方案是扔掉使用ViewModel的额外抽象层,然后编写一些代码隐藏。因此,尽管它对于您的整个应用程序来说是一个很好的结构,但不要陷入强迫所有内容都适合MVVM模式的陷阱。如果您有一些图形复杂的用户控件,在其中包含一些代码后效果更好,那么这就是您应该做的。

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

单元测试Caliburn.Micro ViewModels

来自分类Dev

Caliburn Micro Datagrid绑定

来自分类Dev

Caliburn Micro发布/订阅

来自分类Dev

Caliburn Micro,聚焦MV

来自分类Dev

处理Caliburn Micro的静态IoC

来自分类Dev

Caliburn Micro 2 EventAggregator PublishOnBackgroundThread

来自分类Dev

Caliburn.Micro的UWP约定

来自分类Dev

用Caliburn Micro双击WPFNotifyIcon

来自分类Dev

Caliburn.Micro实现INotifyDataErrorInfo

来自分类Dev

caliburn.micro Condutor not working

来自分类Dev

Caliburn.Micro中的UserControl

来自分类Dev

使用Caliburn.Micro的动作

来自分类Dev

Caliburn.Micro中的UserControl

来自分类Dev

Caliburn.Micro 怎么写?

来自分类Dev

将派生的ViewModels映射到Caliburn.Micro中的基类View

来自分类Dev

Caliburn.Micro是否支持PasswordBox?

来自分类Dev

Caliburn.Micro中的WinRT 8.1设置

来自分类Dev

Caliburn.Micro + Autofac引导程序

来自分类Dev

Caliburn.Micro DisplayRootViewFor抛出NullReferenceException

来自分类Dev

Caliburn.Micro和Bootstrapper / BootstrapperBase类

来自分类Dev

使用Caliburn.Micro重用视图实例

来自分类Dev

Dealing with Caliburn Micro's static IoC

来自分类Dev

未调用Caliburn Micro App Bootstrapper

来自分类Dev

我是否需要屏蔽导体(Caliburn Micro)

来自分类Dev

ReSharper和Caliburn.Micro的约定

来自分类Dev

caliburn.micro Condutor无法正常工作

来自分类Dev

Caliburn.Micro 2.0 .Net目标框架

来自分类Dev

Caliburn Micro IoC / DI工厂方法

来自分类Dev

Caliburn Micro Listbox上下移动项目