【问题标题】:Recommendations for One view that can have two viewmodel modes对可以具有两种视图模型模式的一个视图的建议
【发布时间】:2014-01-14 00:47:34
【问题描述】:

也许我什至没有以正确的方式考虑这一点,所以我愿意接受建议,但我想在创建和编辑时使用完全相同的视图。我不想说创建/编辑实体,因为它比这更复杂。我有嵌套视图(使用 Catel 来实现),所以每个嵌套视图也有它自己的视图模型。但是,根据您是创建还是编辑(在父视图和嵌套视图中),内部状态和执行的命令会有所不同。立即想到的两个想法是:

1) 有一个策略模式视图模型,其中要执行的操作(创建相关或编辑相关)与激活视图的消息一起传递。

2) 不知何故有 2 个可以与同一个视图关联的视图模型以及在它们之间切换的方法。

建议?

【问题讨论】:

    标签: c# wpf mvvm catel


    【解决方案1】:

    MVVM 模式允许您为单个View 提供多个ViewModels。通常它指的是View,它可以有多个动作/状态。在ViewModels 之间切换可以“手动”或使用不同的模式,例如使用策略

    下面我将讨论这两个原则。

    Using the strategy pattern

    此模式允许您拥有一组为特定策略实现算法的类。一个简单的例子:你需要从家里去上班(学习地点等)。这可以通过多种方式完成:

    • 步行去
    • 骑自行车
    • 乘汽车/公共汽车旅行

    所有这些都可以归因于一个策略,其中包括解决特定任务的多种方法。引用《Gang of Four》一书中关于策略模式适用性的内容:

    在以下情况下使用策略模式:

    • 许多相关类仅在行为上有所不同。策略提供了一种使用多种行为之一配置类的方法。

    • 您需要算法的不同变体。例如,您可以定义反映不同空间/时间权衡的算法。当这些变体被实现为算法的类层次结构时,可以使用策略。

    • 算法使用客户端不应该知道的数据。使用策略模式避免暴露复杂的、特定于算法的数据结构。

    • 一个类定义了许多行为,这些行为在其操作中显示为多个条件语句。将相关的条件分支移到它们自己的 Strategy 类中,而不是许多条件。

    结构图:

    Conclusion for Strategy pattern

    如果你对不同类型使用多个操作(Create Remove Update Delete),例如:添加图片,添加用户信息,添加文件,其中也可以有子类型,那么我认为该策略适合您。

    Using several ViewModels for one View

    正如我上面提到的,MVVM 模式允许多个ViewModels 对应一个View。在我看来,最好使用DataTemplate 和选择器DataTemplateSelector 来完成,这将根据其条件返回所需的模板。 DataTemplate 非常适合ViewModel 的视觉呈现。我个人使用动态DataTemplateSelector,例如:

    <ContentControl Name="DynamicContentRightPanel"                            
                    Style="{StaticResource ContentControlRightPanelStyle}"
                    Content="{Binding Path=ContentRightPanelModel.ContentType,
                                      Mode=TwoWay,
                                      UpdateSourceTrigger=PropertyChanged}" />
    

    面板中可能有不同的内容,这取决于用户的选择。

    ContentControlRightPanelStyle

    <Style x:Key="ContentControlRightPanelStyle" TargetType="{x:Type ContentControl}">
        <Setter Property="ContentTemplateSelector" Value="{StaticResource MyTemplateSelector}" />                
    
        <Setter Property="DataTemplateSelectors:DynamicTemplateSelector.Templates">
            <Setter.Value>
                <DataTemplateSelectors:TemplateCollection>
                    <DataTemplateSelectors:Template Value="DateCalculator" 
                                                    DataTemplate="{StaticResource DateCalcTemplate}" />
    
                    <DataTemplateSelectors:Template Value="Test" 
                                                    DataTemplate="{StaticResource TestTemplate}" />
                </DataTemplateSelectors:TemplateCollection>
            </Setter.Value>
        </Setter>
    </Style>    
    

    DynamicTemplateSelector (taken and little reworked from CodeProject)

    public class DynamicTemplateSelector : DataTemplateSelector
    {
        #region Templates Dependency Property
    
        public static readonly DependencyProperty TemplatesProperty =
            DependencyProperty.RegisterAttached("Templates", typeof(TemplateCollection), typeof(DataTemplateSelector),
            new FrameworkPropertyMetadata(new TemplateCollection(), FrameworkPropertyMetadataOptions.Inherits));
    
        public static TemplateCollection GetTemplates(UIElement element)
        {
            return (TemplateCollection)element.GetValue(TemplatesProperty);
        }
    
        public static void SetTemplates(UIElement element, TemplateCollection collection)
        {
            element.SetValue(TemplatesProperty, collection);
        }
    
        #endregion
    
        #region SelectTemplate
    
        public override DataTemplate SelectTemplate(object item, DependencyObject container)
        {
            string myStringItem = (string)item;
    
            if (!(container is UIElement))
            {
                return base.SelectTemplate(item, container);
            }
    
            TemplateCollection templates = GetTemplates(container as UIElement);
    
            if (templates == null || templates.Count == 0)
            {
                base.SelectTemplate(item, container);
            }
    
            foreach (var template in templates)
            {
                if (myStringItem.Equals(template.Value.ToString()))
                {
                    return template.DataTemplate;
                }
            }
    
            return base.SelectTemplate(item, container);
        }
    
        #endregion
    }
    
    #region TemplateCollection
    
    public class TemplateCollection : List<Template>
    {
    
    }
    
    #endregion
    
    #region Template Dependency Object
    
    public class Template : DependencyObject
    {
        public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(string), typeof(Template));
    
        public static readonly DependencyProperty DataTemplateProperty =
           DependencyProperty.Register("DataTemplate", typeof(DataTemplate), typeof(Template));
    
        public string Value
        { get { return (string)GetValue(ValueProperty); } set { SetValue(ValueProperty, value); } }
    
        public DataTemplate DataTemplate
        { get { return (DataTemplate)GetValue(DataTemplateProperty); } set { SetValue(DataTemplateProperty, value); } }
    }
    
    #endregion
    

    Conclusion for several ViewModels / one main View

    如果用户的选择仅限于一种类型的操作,可能是几种,您可以简单地为每个ViewModel / View(在DataTemplate的情况下)创建,因为它会更容易和更方便。

    【讨论】:

    • 虽然这是一个结构良好的答案和一个非常酷的实现(我将在脑海中归档),但它比我需要做的要复杂得多。我仍然认为这对其他人来说很有价值,所以我投了赞成票。我没有需要不同模板的不同数据类型,我基本上有在子视图中显示相关实体的视图。我只需要根据您是在编辑还是创建而执行的命令和触发的事件有所不同。无论如何,感谢您的回答。
    • @BitFiddler:我基本看懂了,所以写了在什么情况下需要使用模式,在什么情况下可以申请One View-N ViewModels。正如我在您的问题中所理解的那样,您寻求的是建议,而不是具体的解决方案。 DataTemplateSelector 只是一个示例,说明如何应用多个ViewModels,以及如何方便、简单地解决View 的动态内容。如果您需要特定的解决方案,请编辑您的问题,其中将包含新问题。
    【解决方案2】:

    在选项 1 为您提供最多选项的情况下,我会尝试尽可能多地重用逻辑。你甚至可以想到这样的场景:

    public void PersonViewModel()
    {
        // Will be used when no model is available - create mode
        public PersonViewModel()
            : this(null) { }
    
        // Will be used when a model is available - edit mode
        public PersonViewModel(IPerson person) 
        { 
            // Here you check if you have a person object, if so, edit 
        }
    }
    

    【讨论】:

    • 我将一个 UnitOfWork 工厂传递给我的所有模型。工作单元能够为我的任何实体类型获取存储库(不确定这是否是正确的做事方式)。这意味着我的视图模型响应“创建”消息或“编辑”消息。创建消息的主体是空的(模型使用注入的工厂来创建具有某些必需默认值的新实例),并且编辑的主体包含对需要更改的实体的引用。理想情况下,我希望视图模型中的所有命令都根据传入的消息进行更改。
    • 我现在正在做的是创建一个“CommandSet”接口。两种实现,一种用于编辑,一种用于创建。这个命令集将作为创建/编辑消息的一部分发送,这样视图模型只需要接收消息并存储命令集,调用它自己收到的命令(视图模型“访问”命令的访问者模式)。这可能很疯狂,但我会看看它是否能在不毛茸茸的情况下完成这项工作。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-10
    • 2013-10-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多