【问题标题】:Want a way to write a class with a property that will return a new instance of a ViewModel想要一种方法来编写一个具有将返回 ViewModel 的新实例的属性的类
【发布时间】:2020-01-29 22:56:13
【问题描述】:

我们有一个 WPF 应用程序,其登录页面列出了大约十几个按钮,所有按钮都指向该类型的新视图/视图模型。它变得笨拙。我们有一个视图模型,列出了所有这些,基本上如下所示:

    private void ExecuteViewProgramCommand()
    {
        OpenViewMessage message = new OpenViewMessage();
        CurrentViewModel = message.ViewModel = ViewModelLocator.ProgramVM;
        Messenger.Default.Send<OpenViewMessage>(message);
    }

我从不喜欢这样做的方式,因为它违反了 DRY 原则。上面第二行代码中唯一发生变化的地方是ViewModelLocator.ProgramVM。我的任务是重做登录页面,使其更有条理,我们将添加更多启动按钮。我认为使用依赖注入会更好。此外,我正在尝试解决重新设计显示的需要,以便它在列表中,而不是按字母顺序分散的按钮。

首先我想出了这个类:

public class Tile
{
    public string ModuleName { get; set; }
    public NamedViewModelBase ModuleViewModel { get; set; }
}

(NamedViewModelBase 是所有视图模型通用的视图模型的名称。)然后我声明了一个单元测试来测试它并在单元测试中声明它:

List<Tile> tiles = new List<Tile>()
{
    new Tile()
    {
        ModuleName = "Program",
        ModuleViewModel = ViewModelLocator.ProgramVM
    },
    new Tile()
    {
        ModuleName = "Organization",
        ModuleViewModel = ViewModelLocator.OrganizationVM
    }
}

但这很快就证明这是错误的。 ViewModelLocator.ProgramVM 的设置器中的分配将实例化 Program 的视图模型。我不希望这样,我宁愿调用实例化它,比如我们在 ViewModelLocator 中:

static public ProgramViewModel ProgramVM
{
    get
    {
        if (ServiceLocator.IsLocationProviderSet)
        {
            SimpleIoc ioc = ServiceLocator.Current as SimpleIoc;
            return ioc.GetInstanceWithoutCaching<ProgramViewModel>(Guid.NewGuid().ToString());
        }
        else
        {
            return null;
        }
    }
}

所以,我认为我必须更改 Tile 类以将 ModuleViewModel 属性声明为如下所示:public NamedViewModelBase ModuleViewModel { get; }。但我不知道在定义列表时如何实例化它。解决这个问题的正确方法是什么?

【问题讨论】:

  • 如果您的 VM 具有最少的 ctors(没有 db 调用等,只有 init),那么您可以创建它们的 ObservableCollection 并分配一个按钮作为列表视图的模板,以便以这种方式使用和呈现它们。我知道 IoC 给人的印象是自动解析解决方案,但它实际上只是一组美化的全局变量。
  • 你的问题有点不清楚。鉴于您当前的Tile 实现,您可以将其ModuleViewModel 属性设置为任何视图模型。您的问题到底是什么?
  • mm8,问题在于确保如果用户点击 Program 3 次,他们总是会获得 ProgramViewModel 的新实例。我实现Tile 类的方式,他们会得到相同的实例,这是我不能允许的。
  • 如果我展示我们如何实例化一个新的虚拟机,比如 ProgramVM,这可能是最好的。以下代码来自我们的 ViewModelLocator: static public ProgramViewModel ProgramVM { get { if (ServiceLocator.IsLocationProviderSet) { SimpleIoc ioc = ServiceLocator.Current as SimpleIoc;返回 ioc.GetInstanceWithoutCaching(Guid.NewGuid().ToString()); } 否则 { 返回空值; } } }

标签: c# wpf mvvm-light


【解决方案1】:

这将是伪 codish 建议,与您已经在同一轨道上:

假设 BaseViewModel 是所有单个 VM 的基类

  1. 创建字典
  2. 在应用程序启动期间填写此字典(看起来像您的磁贴列表)

    public void PreCreateVMs() { 字典[键] = 新的 ConcreteViewModelType(); // 在这里继续添加新的虚拟机 }

  3. 在 xaml 中,将所有按钮绑定到相同的命令,该命令采用字符串参数(或使用 Enum 即兴发挥)。为每个按钮传递正确的字符串键。 喜欢:帐户按钮单击应该启动 AccountVM,它与字典中的“AccountVM”键一起存储。

  4. 在命令处理程序中 - 使用字符串,查找字典找到正确的 ViewModel 并将此对象分配给 CurrentViewModel

从维护的角度来看 - 添加新 ViewModel 所需的只是使用新按钮更新 xaml,分配正确的命令参数字符串。使用此字符串键并在 PreCreateVMs 方法中添加正确的 VM。

【讨论】:

    【解决方案2】:

    我重新设计了 Tile 类。我相信我需要的是第二个参数是一个命令。我在问这是否会做得更好。这是 Tile 的新定义以及我如何尝试实现它的示例:

    public class Tile
    {
        public string ModuleName { get; set; }
        //public NamedViewModelBase ModuleViewModel { get; set; }
        public Action ThisCommand { get; set; }
    }
    

    以下是我尝试将其实现为列表的方式:

    List<Tile> tiles = new List<Tile>()
    {
        new Tile()
        {
            ModuleName = "Program",
            ThisCommand = () => 
            {
                if (ServiceLocator.IsLocationProviderSet)
                {
                    SimpleIoc ioc = ServiceLocator.Current as SimpleIoc;
                    ioc.GetInstanceWithoutCaching<ProgramViewModel>(Guid.NewGuid().ToString());
                }
            }
        },
        new Tile()
        {
            ModuleName = "Organization",
            ThisCommand = () =>
            {
                if (ServiceLocator.IsLocationProviderSet)
                {
                    SimpleIoc ioc = ServiceLocator.Current as SimpleIoc;
                    ioc.GetInstanceWithoutCaching<OrganizationViewModel>(Guid.NewGuid().ToString());
                }
            }
        }
    };
    

    我在正确的轨道上吗?我应该将tiles 定义为字典吗?

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-07-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多