1

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

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

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

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

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

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

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

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

4

1 回答 1

1

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

  1. 暴露一个 MessageViewModel 的集合,因此您可以将此集合绑定到 ListView 的 itemsSource。
  2. (可选),您在特定列表视图上有一个样式(或内容展示器或数据模板),您希望重用它。这部分可能还包括代码隐藏、触发器等。

您不应该将两者混为一谈。

#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>
于 2013-02-25T18:43:03.183 回答