在MVVM中运行时将WPF ListBox ItemsSource从ObservableCollection <t>切换到CollectionViewSource </t>

时间:2011-05-22 13:19:56

标签: wpf mvvm mvvm-light collectionviewsource

我在一个MVVM项目中有一个ViewModel,它绑定到一个WPFView,它包含一个列表框,在这个列表框中,当你开始搜索时异步加载数据。 我的搜索返回的数据包含在不同日期购买的相同商品的价格。 我需要按列表升序列出我的列表框中的项目:一旦我的BackgroundWorker返回项目,这些项目就会异步添加到我的列表框中并按价格排序,让我看到最快的价格。

我找到的更好的解决方案是在我的ViewModel中有一个ObservableCollection并将它绑定到我的ListBox的ItemsSource 我有一个启动异步搜索的BackgroundWorker;我订阅了进行搜索的对象的DataReceived EventHandler,我按这样通知用户界面:

void sniper_DataReceived(object sender, TEventArgs e)
    {
        Action dispatchAction = () => this.Results.Add(e.T);
        _currentDispatcher.BeginInvoke(dispatchAction);
    }

通过Dispatcher

private readonly Dispatcher _currentDispatcher;

这对我来说似乎没问题,但是没有按照我的需要订购这些项目,所以我发现CollectionViewSource正是我所需要的,只是简单的方式。

这是问题所在:

如果我将listbox的datacontext设置为CollectionViewSource,我的设计时间能力较小,我会在列表框中看到我的设计时数据,但是我在Blend的Data选项卡中丢失了DataContext。

所以我做了一些我认为有点脏的事情: 我使用x:Name属性命名我的ListBox,并在我的MainWindow.xaml中添加了一些代码,以便在运行时交换我的命名列表框的数据源,如下所示:

public MainWindow()
    {
        InitializeComponent();
        Closing += (s, e) => ViewModelLocator.Cleanup();

        #region CollectionViewSource Escamotage
        if (!ViewModelLocator.MainStatic.IsInDesignMode)
        {
            var cvs = new CollectionViewSource() { Source = ViewModelLocator.MainStatic.Results };
            cvs.SortDescriptions.Add(new SortDescription("LowestPrice", ListSortDirection.Ascending));
            this.TrainsListBox.ItemsSource = cvs.View; 
        }
        #endregion
    }

你认为这可以被视为罪吗?

1 个答案:

答案 0 :(得分:1)

您可以将集合视图的源绑定到observablecollecion。我的问题是:如果你正在使用mvvm,为什么你在看起来像代码隐藏的情况下做所有这些?

ViewModel构造函数:

 public PrimarySearchViewModel()
            {
                this.SearchResultsCVS = new CollectionViewSource();

                if (IsInDesignMode)
                {
                    DesignMode_CreateSearchResults();

                    // Code runs in Blend --> create design time data.
                }
                else
                {
                    //Messenger.Default.Register<IEnumerable<ReadmitPatientList>>(this, MessageTypes.EXECUTESEARCHREQUEST, RefreshSearchResults);
                    //Messenger.Default.Register<MessageTypes.EXECUTESEARCHREQUEST>>(this,ICollection<ReadmitPatientList>,RefreshSearchResults);
                    Messenger.Default.Register<Messages.DisplayReadmitPatientListMessage>(this, onReciveDisplayReadmitPatientListMessage);
                    Messenger.Default.Register<WavelengthIS.Core.Messaging.SaveNotification<QuestionairreViewModel>>(this, sn => ClearSearchResults());
                    // Code runs "for real": Connect to service, etc...
                }

            }

我通常使用设计时服务来创建我的设计时数据:但在这种情况下,我只是做了一个快速而又脏的复制和粘贴:

private void DesignMode_CreateSearchResults()
            {
                this.SearchResults = new ObservableCollection<ReadmitPatientListViewModel>();

                this.SearchResults.Add(new ReadmitPatientListViewModel(new ReadmitPatientList()
                {
                    PatientID = 0000000,
                    PatientName = "Test Patient",
                    PatientDOB = Convert.ToDateTime("01/01/2010"),
                    OriginalAdmitDate = Convert.ToDateTime("01/01/2010 00:00:00"),
                    OriginalReason = "Becauselkahsdfkahsfkahsf",
                    OriginalVisitNumber = "0000000",
                    ReAdmitDate = Convert.ToDateTime("01/01/2010 00:00:00"),
                    ReAdmitReason = ";aasfkahsfashfa;lsfas",
                    ReAdmitVisitNumber = "9999999"
                }
                   ));
                this.SearchResults.Add(new ReadmitPatientListViewModel(new ReadmitPatientList()
                {
                    PatientID = 0000000,
                    PatientName = "Test Patient",
                    PatientDOB = Convert.ToDateTime("01/01/2010"),
                    OriginalAdmitDate = Convert.ToDateTime("01/01/2010 00:00:00"),
                    OriginalReason = "Becauselkahsdfkahsfkahsf",
                    OriginalVisitNumber = "0000000",
                    ReAdmitDate = Convert.ToDateTime("01/01/2010 00:00:00"),
                    ReAdmitReason = ";aasfkahsfashfa;lsfas",
                    ReAdmitVisitNumber = "9999999"
                }
                   ));
                this.SearchResults.Add(new ReadmitPatientListViewModel(new ReadmitPatientList()
                {
                    PatientID = 0000000,
                    PatientName = "Test Patient",
                    PatientDOB = Convert.ToDateTime("01/01/2010"),
                    OriginalAdmitDate = Convert.ToDateTime("01/01/2010 00:00:00"),
                    OriginalReason = "Becauselkahsdfkahsfkahsf",
                    OriginalVisitNumber = "0000000",
                    ReAdmitDate = Convert.ToDateTime("01/01/2010 00:00:00"),
                    ReAdmitReason = ";aasfkahsfashfa;lsfas",
                    ReAdmitVisitNumber = "9999999"
                }
                   ));

                SearchResultsCVS_Refresh();
            }
private void SearchResultsCVS_Refresh()
        {
            SearchResultsCVS.Source = this.SearchResults;
            SearchResultsCVS.SortDescriptions.Clear();

            SearchResultsCVS.SortDescriptions.Add(new System.ComponentModel.SortDescription("PatientLastName", System.ComponentModel.ListSortDirection.Ascending));

            SearchResultsCVS.View.Refresh();
        }

我使用ViewModels的ObservableCollections。 OC的通知事件仅触发从集合中添加或删除的项目,通过使用实际列表项的vm,如果需要,您将获得项目属性的更改通知。

您还需要确保自己的ViewModelLocator设置正确。我现在发现了几个使用MVVMLight但没有使用其中一些最强大功能的帖子。如果你使用它作为它的设计使用,它的工作就像它应该工作......我可以证明这一点。

 <!--Global View Model Locator-->
                    <local:ViewModelLocator x:Key="Locator"
                                            d:IsDataSource="True" />

我相信IsDataSource属性告诉Blend将它放在DataTab上......但我不会使用混合这么多来进行我的Datamanipulations所以我并不担心它。