Wpf 可重用项控件(如列表视图)和#x2B;MVVM

Wpf 可重用项控件(如列表视图)和#x2B;MVVM,wpf,mvvm,itemscontrol,Wpf,Mvvm,Itemscontrol,我正在用WPF构建一个MVVM应用程序,它使用了许多相对复杂的列表视图。我采用了一种模式,列表视图绑定到的集合是视图模型对象的集合,而不是底层模型对象的列表——我通过将数据绑定到一个完全独立的列来实现这一点,该列中填充的代码有点像这样 var itemsSource = messages.Select(i => new MessageViewModel(i)); 在这种情况下,列表视图向用户显示消息对象的列表。这工作正常,但是相当笨拙——特别是在处理集合更改事件时 现在我想在我的应用程序

我正在用WPF构建一个MVVM应用程序,它使用了许多相对复杂的列表视图。我采用了一种模式,列表视图绑定到的集合是视图模型对象的集合,而不是底层模型对象的列表——我通过将数据绑定到一个完全独立的列来实现这一点,该列中填充的代码有点像这样

var itemsSource = messages.Select(i => new MessageViewModel(i));
在这种情况下,列表视图向用户显示
消息
对象的列表。这工作正常,但是相当笨拙——特别是在处理集合更改事件时

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

  • 创建从
    ListView
    派生的列表视图,并将数据绑定到类型为
    MessageViewModel
  • 创建一个控件,该控件将数据绑定到一组
    Message
    对象,这些对象包含绑定到某个内部构造的
    MessageViewModel
第一个选项要求使用该控件的每个人都运行构建和维护
MessageViewModel
集合的笨拙代码,第二个选项封装了此视图模型集合的维护,但这意味着我需要重新实现
ListView
的任何成员,该成员公开集合中的基础项,以便将它们转换回原始
消息类型

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


有没有更好的方法来处理WPF items基于控件的视图,从而允许在MVVM应用程序中重用这些视图?

在我看来,有两件事需要重用:

  • 公开MessageViewModel的集合,以便可以将此集合绑定到ListView的itemsSource
  • (可选)在特定列表视图上有一个样式(或内容演示器或数据模板),您希望重用该样式。这部分还可能包括代码隐藏、触发器等
  • 你不应该把两者混为一谈

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

    #1是一个实现集合、InotifyCollectionChanged和INotifyPropertyChanged的类。最好的方法(也是最简单的方法)是仅将其环绕在ObservableCollection上。在构造中,执行“选择”方法。然后有记账的方法

    下面是一些示例(工作!)代码。注意,视图没有代码隐藏。我把这两个列表放在网格中两次。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; }
        }
    }
    
    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 );
            }
        }
    }
    
    =======ViewModel.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; }
        }
    }
    
    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 );
            }
        }
    }
    
    使用系统;
    使用System.Collections.Generic;
    使用System.Linq;
    使用System.Collections.ObjectModel;
    使用系统组件模型;
    名称空间SO
    {
    类MessageVM:INotifyPropertyChanged
    {
    公共事件属性更改事件处理程序属性更改;
    私有消息m_模型;
    公共消息VM(消息模型){
    m_模型=模型;
    }
    私有void raize(字符串属性){
    房地产变更(这是指新的房地产变更开发项目(“prop”);
    }
    来自的公共字符串{
    获取{return m_model.from;}
    set{m_model.from=value;raize(“from”);}
    }
    公共字符串到{
    获取{return m_model.to;}
    设置{m_model.subject=value;raize(“to”);}
    }
    公共字符串主题{
    获取{return m_model.subject;}
    set{m_model.subject=value;raize(“subject”);}
    }
    收到的公共日期时间{
    获取{return m_model.received;}
    set{m_model.received=value;raize(“received”);}
    }
    }
    类FolderVM:ObservableCollection
    {
    公共FolderVM(IEnumerable模型)
    :base(models.Select(msg=>newmessagevm(msg)))
    {
    }
    }
    类样本数据
    {
    //静态公共FolderVM文件夹{get;set;}
    静态公共FolderVM文件夹;
    静态采样数据()
    {
    //创建一个示例模型
    列表模型=新列表();
    添加(新消息{from=“Bill”,to=“Steve”,subject=“Resusable Items Control”,received=DateTime.Now.AddDays(-4)});
    添加(新消息{from=“Steve”,to=“Bill”,subject=“Resusable Items Control”,received=DateTime.Now.AddDays(-3)});
    添加(新消息{from=“Bill”,to=“Jeff”,subject=“stack”,received=DateTime.Now.AddDays(-2)});
    //初始化视图模型
    folder=新FolderVM(型号);
    }
    }
    }
    
    =======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>
    

    在我看来,有两件事需要重用:

  • 公开MessageViewModel的集合,以便可以将此集合绑定到ListView的itemsSource
  • (可选)在特定列表视图上有一个样式(或内容演示器或数据模板),您希望重用该样式。这部分还可能包括代码隐藏、触发器等
  • 你不应该把两者混为一谈

    #2可以通过应用于列表视图的样式或数据模板来实现。就个人而言,我喜欢将专用类定义为消息的集合