代码之家  ›  专栏  ›  技术社区  ›  Jack Ukleja

与“实时”集合和属性同步的双向视图模型

  •  6
  • Jack Ukleja  · 技术社区  · 15 年前

    我最近对视图模型(VM)有点纠结。

    就像 this guy 我得出的结论是,我需要在我的虚拟机上公开的集合通常包含与在我的业务对象上公开的集合不同的类型。

    因此,这两种类型之间必须有双向映射或转换。(只是为了使事情复杂化,在我的项目中,这些数据是“实时的”,这样一旦您更改了一个属性,它就会被传输到其他计算机上)

    我可以用这样的框架来处理这个概念 Truss 尽管我怀疑里面会有令人讨厌的惊喜。

    不仅必须转换对象,还需要在这两个集合之间进行同步。(只是为了使事情复杂化,我可以想到,在这种情况下,VM集合可能是业务对象集合的子集或联合,而不仅仅是1:1的同步)。

    我可以看到如何使用复制的可观测集合或类似clinq的东西进行单向“实时”同步。

    问题就变成了:创建/删除项目的最佳方法是什么?

    双向同步似乎不在卡上-我没有发现这样的例子,并且唯一支持类似远程操作的类是ListCollectionView。双向同步是否是一种明智的方法,可以重新添加到业务对象集合中?

    我所看到的所有样品似乎都无法解决这个“复杂”的问题。

    所以我的问题是:你如何解决这个问题?是否有一些技术可以从虚拟机更新模型集合?最好的一般方法是什么?

    5 回复  |  直到 13 年前
        1
  •  3
  •   NathanAW    15 年前

    我也在努力通过MVVM与WPF一起使用两个集合的双向同步。我博客 MVVM: To Wrap or Not to Wrap? How much should the ViewModel wrap the Model? (Part 1) MVVM: To Wrap or Not to Wrap? Should ViewModels wrap collections too? (Part 2) 关于这个问题,包括一些显示双向同步的示例代码。然而,正如各员额所指出的,执行情况并不理想。我认为这是概念的证明。

    我喜欢 BLINQ , CLINQ ,和 Obtics Alex_p发布的框架。这是一个非常好的方法,以获得同步贝瓦瓦伊奥的一面。也许另一面(从虚拟机到模型)可以通过另一条路径实现?我刚刚张贴 part 3 在我的博客上讨论了其中的一些问题。

    据我所见,在Linq语句将数据投影到新结构的情况下,不支持通过blinq和clinq实现双向。

    但是,在Linq查询返回与基础集合相同的数据类型的情况下,它看起来确实支持双向同步。这更像是一个过滤场景,与包装模型中数据的ViewModel的用例不匹配。

        2
  •  4
  •   Thomas daign    15 年前

    就我个人而言,我在我的模型和视图模型中使用了一个可观察的集合。

    class Model
    {
        public ObservableCollection<Foo> Foos;
    }
    
    class ViewModel
    {
        public Model Model;
        public ObservableCollection<FooView> Foos;
    
        public ViewModel()
        {
            Model.Foos.CollectionChanged += OnModelFoosCollection_CollectionChanged;
        }
    
        void OnModelFoosCollection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
           Foo re;
    
           switch (e.Action)
           {
              case NotifyCollectionChangedAction.Add:
                 re = e.NewItems[0] as Foo;
                 if (re != null)
                    AddFoo(re);  //For other logic that may need to be applied
                 break;
              case NotifyCollectionChangedAction.Remove:
                 re = e.OldItems[0] as Foo;
                 if (re != null)
                    RemoveFoo(re); 
                 break;
              case NotifyCollectionChangedAction.Reset:
                 Foos.Clear();
                 /* I have an AddRange in an ObservableCollection-derived class
                    You could do Model.Foo.ForEach(ree => AddFoo(ree));
                 */
                 var converter = 
                    from ree in Model.Foo
                    select new FooView(ree);
                 Reports.AddRange(converter); 
                 break;
              default:
                 //exercise for the reader :)
                 s_ILog.Error("OnModelFoosCollection_CollectionChangedDid not deal with " + e.Action.ToString()); 
                 break;
           }
        }   
    
        void AddFoo(Foo f)
        {
            Foos.Add(new FooView(f));
        }
    
        void RemoveFoo(Foo f)
        {
           var match = from f in Foos
              where f.Model == f  //if you have a unique id, that might be a faster comparison
              select f;
           if(match.Any())
              Foos.Remove(match.First());
        }
    }
    

    现在,当您从模型的foo集合中删除某些内容时,它将自动删除相应的fooview。这与我对这类事情的看法是一致的。如果我想删除一些东西,模型就是真正需要删除的地方。

    这感觉像很多代码,但实际上并没有那么多。我相信有人可以构建这个的通用版本,但是在我看来,你最终总是想要处理元素添加/删除的定制逻辑。

        3
  •  3
  •   Alex_P    14 年前

    唯一可能需要双向同步的情况是,用于可视化虚拟机集合的控件不让您知道用户创建或删除项目的意图。也就是说,控件直接处理您的虚拟机集合,您知道该项已被添加/删除的唯一方法是监视虚拟机集合。如果不是这样,那么可以实现单向同步,并直接在模型集合上添加/删除项。

    编辑:以wpf datagrid为例 绑定到可观测集合的控件 项视图模型的。如果其 CanUserAddRows属性设置为true 用户开始输入 底部的空行,数据报ID 将使用您的 创建松散项的itemViewModel 然后将其添加到 收集。没有迹象 从要添加项目的DG 收藏.c 我想不出任何 其他复杂的控制 足以将项目添加到 自己收集。
    相反 场景是当您有ListView时 绑定到集合和命令 表示用户的意图 添加新项-然后在命令处理程序中 只需将新项添加到数据模型 让单向同步完成 这项工作。在这种情况下,ListView不是 可以添加到集合中 礼物。

    至于同步过程本身,请看 Bindable LINQ 项目-它可以最小化数量代码并提高可读性。例如,Tom发布的代码将转换为如下内容:

    class ViewModel
    {
      public Model Model;
      public ObservableCollection<FooView> Foos;
      public ViewModel()
      {
        Foos = from foo in Model.Foos.AsBindable()
               select new FooView(foo);
      }
    }
    

    编辑2:在使用了b-linq一段时间之后,现在我应该说您可能会遇到性能问题。我用它来同步相对较大的集合(数百个元素),每秒添加和删除数十个元素,我不得不放弃它,按照Tom的建议实现同步。
    不过,我仍然在项目的那些部分使用b-linq,在这些部分中,集合很小,性能不是问题。

        4
  •  1
  •   Aran Mulholland JohnnyAce    15 年前

    我已经编写了一些助手类,用于将可观察的业务对象集合包装在它们的视图模型对应物中。 here 或许应该把它延伸到另一个方向。一直在寻找贡献…

        5
  •  1
  •   DaniCE    15 年前

    我提出了一个基于MVVM的通用撤消/重做框架,它使用了一些与您描述的问题相关的技术。它使用实现此接口的集合:

    public interface MirrorCollectionConversor<V, D>
    {
       V GetViewItem(D modelItem, int index);
       D GetModelItem(V viewItem, int index);
    }
    

    (V表示视图模型项,D表示模型项)

    使用此接口,它会在模型集合更改时自动同步ViewModel集合。如果更改了ViewModel,则只需将更改重定向到模型集合。

    getviewitem函数在viewModel对象与模型对应对象的关系方面提供了一些灵活性。

    你可以找到细节 here .

    (我承认施工相当复杂,我很乐意听取建议)。