【问题标题】:Inject views into ItemsControl depending on object type根据对象类型将视图注入 ItemsControl
【发布时间】:2011-08-07 07:45:09
【问题描述】:

我有一个服务返回一个派对类型的数组。党有两个子类型,个人和组织。我正在我的 WPF 应用程序(Prism、MVVM)中从视图模型中使用此服务。在这个视图模型的构造函数中,我填充了一个 Party 类型的 observable 集合:

public PhoneBookViewModel(IPhoneBookService phoneBookProxy)
{
    _phoneBookProxy = phoneBookProxy;

    var parties = _phoneBookProxy.GetAllParties();
    _parties = new ObservableCollection<Party>(parties.ToList());
}

到目前为止一切顺利。在我的 PhoneBookView 中,我有一个绑定到此集合的 ItemsControl。在这个控件中,我想通过使用另一个视图(及其视图模型)来呈现每个派对。所以当Party是Person类型时,注入PersonView并将Party对象传递给PersonViewModel的构造函数,当Party是Organization类型时,渲染OrganizationView,等等......你得到了图片(或?)。

但我不知道如何在 XAML 中执行此操作。有任何想法吗? 这可能不是最好的方法,所以如果你能推荐更好的方法,请赐教:-)

谢谢!

【问题讨论】:

    标签: wpf inheritance mvvm view viewmodel


    【解决方案1】:

    让我们从模型的角度来检查一下:


    假设我们有 2 种不同类型的视图,1 种视图模型:

    ViewA --> 使用 DataTempate/DataTemplateSelector 在项目控件中创建,绑定 > 到 ViewModelA

    ViewB --> 使用 DataTempate/DataTemplateSelector 在项目控件中创建,绑定到 ViewModelA

    如果两个视图都绑定到同一个视图模型,您最终会得到相同的视图。


    让我们用 2 种不同类型的视图和 2 种不同类型的视图模型再试一次:

    ViewA --> 使用 DataTempate/DataTemplateSelector 在项控件中创建,绑定到 ViewModelA --> 绑定到 ModelA

    ViewB --> 使用 DataTempate/DataTemplateSelector 在项目控件中创建,绑定到 ViewModelB --> 绑定到 ModelB

    这是可能的。


    现在,如果您像这样(伪代码)对视图模型和模型进行建模:

    public PhoneBookViewModel
    {
        public PhoneBookViewModel()
        {
            _parties = new ObservalbeCollection<PartyViewModel>();
        }
    
        private PhoneBook _dataContext;
    
        // This is the property the VM uses to access the model
        public PhoneBook DataContext
        {
            get { return _dataContext; }
            set
            {
                if (_dataContext != null)
                {
                    _dataContext.Parties.CollectionChanged -= OnModelPartiesChanged;
                }
                _dataContext = value;
                if (_dataContext != null)
                {
                    _dataContext.Parties.CollectionChanged += OnModelPartiesChanged;
                }
            }
        }
    
        private ObservableCollection<PartyViewModel> _parties;
    
        // This is the property the view uses to access the collection of VM parties
        public ObservableCollection<PartyViewModel> PartiesViewModels { get { return _parties; } }
    
        private void OnModelPartiesChanged(...)
        {
            // Add/remove VMs to/from PartiesViewModels here
        }
    }
    
    // Model
    public PhoneBook
    {
        public PhoneBook()
        {
            _parties = new ObservalbeCollection<Party>();
        }
    
        private ObservableCollection<Party> _parties;
    
        // This is the property the VM uses to access the model's parties
        public ObservableCollection<Party> Parties { get { return _parties; } }
    }
    
    public PersonViewModel : PartyViewModel
    {
        new Person DataContext { get; set; }
    }
    
    public PartyViewModel
    {
        public Party DataContext { get; set; }
    }
    

    那么您将获得每个模型项的正确类型的虚拟机, 视图将绑定到 VM 项,而不是模型项。


    视图的数据模板:

    <DataTemplate x:Target={x:Type myVmNamespace:PersonViewModel}">
        <PersonView/>
    </DataTemplate>
    
    <DataTemplate x:Target={x:Type myVmNamespace:GroupViewModel}">
        <GroupView/>
    </DataTemplate>
    

    视图的项目控件:

    <!-- Bind to Parties property of PhoneBookVM -->
    <!-- Uses datatemplates for items -->
    <ListView ItemsSource={Binding Parties}"/>
    

    【讨论】:

    • 我没有详细说明管道的所有细节(例如,集合后面的只读字段,以保持 OnCollectionChanged 注册到正确的集合)。但是这种方法效果很好(已经用过很多次了)。
    • 这对我来说没有多大意义 :-) 您的 PhoneBookViewModel 中的 PhoneBook DataContext 是什么,它与两个 PartyViewModel 有什么关系?我不明白。
    • 添加了更多细节。基本上,VM 连接到 M 的方式类似于 V 连接到 VM 的方式。
    • 谢谢丹尼。我现在很忙,但我会尽快看看这个。只是想让你知道 - 我没有忘记它:-)
    【解决方案2】:

    为您的数据类型配置数据模板以呈现 xaml。

    【讨论】:

    • 怎么样?以及如何将参数传递给视图模型的构造函数?
    【解决方案3】:

    如果您使用的是 Prism 和 MVVM,那么您正在将一个命令绑定到您的 ItemsControl 与所有各方。

    此命令的类型为DelegateCommand&lt;Party&gt;。该命令正在执行的 Delegate 内部,如下所示:

    private void PartyNavigate(Party party)
    

    只需检查聚会是否是任何子类型,然后在您的RegionManager 区域调用RequestNavigate 到特定视图。

    然后如何传递实际上下文将成为一个问题,您可以查看 Prism 附带的 MVVM RI,它以 StateHandler 的形式很好地解决了该问题,或者您可以构建您的拥有集中的 DataManager,您可以在其中保存这些内容的状态,以及从 WebServices 获取的缓存内容等。在使用 WPF 和 WCF 构建智能客户端 2 年后,我可以告诉您,您最终将需要构建自己的 DataManager,它如果您已经在使用 EntityFramework 并且大部分是从 EDM 生成的,那么这将不是什么大问题。

    【讨论】:

      【解决方案4】:

      您的 ItemsControl ItemsSource 集合由 PhoneBookViewModel 填充。所以剩下的唯一事情就是告诉 WPF 这个集合的每个项目应该如何呈现。这可以通过创建 DataTemplate 轻松实现。

       <DataTemplate DataType="{x:Type PersonViewModel}"> 
           <MyPersonView/> 
       </DataTemplate>
      

      【讨论】:

      • 是的,但我需要将特定实例传递给视图。我该怎么做?
      猜你喜欢
      • 2012-07-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多