可重用的ItemsControl(例如ListViews)+ MVVM

时间:2013-02-25 12:04:41

标签: wpf mvvm itemscontrol

我正在使用WPF构建MVVM应用程序,该应用程序使用了许多相对复杂的列表视图。我采用的模式是列表视图绑定的集合是View-Model对象的集合,而不是底层模型对象的列表 - 我通过数据绑定到一个完全独立的列来完成此操作看起来有点像这样的代码

var itemsSource = messages.Select(i => new MessageViewModel(i));

在这种情况下,列表视图向用户显示Message个对象的列表。这样可行,但是相当笨重 - 特别是在处理集合更改事件时。

现在我想在我的应用程序的其他地方重用这个ListView,以一致的方式向用户显示不同的消息列表 - 我可以看到的选项是

  • 创建一个派生自ListView的列表视图,数据绑定到MessageViewModel类型的集合
  • 创建一个数据绑定到Message个对象集合的控件,这些对象包含或派生自绑定到某个内部构造的MessageViewModel
  • 集合的列表视图数据

第一个选项要求使用该控件的每个人运行构建和维护MessageViewModel集合的笨重代码,第二个选项封装此视图模型集合的维护但是意味着我需要重新实现任何ListView的成员,它公开集合中的基础项目,以便将它们转换回原始的Message类型。

我有许多类似的列表视图,它们具有类似的可重用性问题。

是否有更好的方法来处理基于WPF ItemsControl的视图,允许在MVVM应用程序中重用这些视图?

1 个答案:

答案 0 :(得分:1)

在我看来,您想要重复使用两件事:

  1. 公开MessageViewModel的集合,以便将此集合绑定到ListView的itemsSource。
  2. (可选),您要在特定列表视图中使用样式(或内容演示者或数据模板),您希望重复使用该样式。这部分还可能包括代码隐藏,触发器等。
  3. 你不应该混淆两者。

    #2可以使用您将应用于列表视图或数据模板的样式来实现。就个人而言,我喜欢将专用类定义为MessageViewModel的集合,并在您的数据模板中将TargetType设置为该类。

    #1是一个实现Collection,INotifyCollecitonChanged和INotifyPropertyChanged的类。最好的方法(也是最简单的方法)就是将它包装在ObservableCollection周围。在构造中,执行Select方法。然后有书籍保存方法。

    下面是一些示例(工作!)代码。请注意,视图后面没有代码。我把两个列表放在网格中两次。 ContentControl和DataTemplate的用法是我的风格 - 还有很多其他方法可以做到。

    ======= Model.cs ====

    using System;
    
    namespace SO
    {
        class Message
        {
            public string from { get; set; }
            public string to { get; set; }
            public string subject { get; set; }
            public DateTime received { get; set; }
        }
    }
    

    ======= ViewModel.cs ====

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Collections.ObjectModel;
    using System.ComponentModel;
    
    namespace SO
    {
        class MessageVM : INotifyPropertyChanged
        {
            public event PropertyChangedEventHandler PropertyChanged;
            private Message m_model;
    
            public MessageVM( Message model ) {
                m_model = model;
            }
    
            private void raize( string prop ) {
                PropertyChanged( this, new PropertyChangedEventArgs("prop") );
            }
    
            public string from {
                get { return m_model.from; }
                set { m_model.from = value; raize("from"); }
            }
    
            public string to {
                get { return m_model.to; }
                set { m_model.subject = value; raize("to") ); }
            }
    
            public string subject {
                get { return m_model.subject; }
                set { m_model.subject = value; raize("subject") ); }
            }
    
            public DateTime received {
                get { return m_model.received; }
                set { m_model.received = value; raize("recieved") ); }
            }
    
        }
    
        class FolderVM : ObservableCollection<MessageVM>
        {
            public FolderVM( IEnumerable<Message> models )
                :base( models.Select( msg => new MessageVM(msg) ) )
            {
            }
        }
    
    
        class SampleData
        {
            //static public FolderVM folder { get; set; }
    
            static public FolderVM folder;
    
    
    
            static SampleData( )
            {
                // create a sample model
                List<Message> model = new List<Message>();
                model.Add( new Message { from = "Bill", to = "Steve", subject = "Resusable Items Control", received = DateTime.Now.AddDays(-4) } );
                model.Add( new Message { from = "Steve", to = "Bill", subject = "Resusable Items Control", received = DateTime.Now.AddDays( -3 ) } );
                model.Add( new Message { from = "Bill", to = "Jeff", subject = "stack", received = DateTime.Now.AddDays( -2 ) } );
    
                // initialize the view model
                folder = new FolderVM( model );
            }
        }
    }
    

    ======= MainWindow.xaml ====

    <Window x:Class="Paf.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:src="clr-namespace:SO"
            Title="MainWindow" Height="350" Width="525"
            >
    
        <Window.Resources>
            <DataTemplate DataType="{x:Type src:FolderVM}">
                <ListView ItemsSource="{Binding}">
                    <ListView.View>
                        <GridView>
                            <GridViewColumn Header="from" Width="80" DisplayMemberBinding="{Binding Path=from}" />
                            <GridViewColumn Header="to" Width="80" DisplayMemberBinding="{Binding Path=to}" />
                            <GridViewColumn Header="subject" Width="200" DisplayMemberBinding="{Binding Path=subject}" />
                            <GridViewColumn Header="received" Width="160" DisplayMemberBinding="{Binding Path=received}" />
                        </GridView>
                    </ListView.View>
                </ListView>
            </DataTemplate>        
        </Window.Resources>
    
    
        <StackPanel>
            <ContentControl Content="{Binding Source={x:Static src:SampleData.folder}}" />
            <ContentControl Content="{Binding Source={x:Static src:SampleData.folder}}" />
        </StackPanel>
    
    </Window>