开发者

Switching a WPF ListBox ItemsSource from ObservableCollection<T> to CollectionViewSource at runtime in MVVM

开发者 https://www.devze.com 2023-03-08 01:29 出处:网络
I have a ViewModel in a MVVM project that is bound to a WPFView that contains a listbox where data is loaded asynchronously when you start a search.

I have a ViewModel in a MVVM project that is bound to a WPFView that contains a listbox where data is loaded asynchronously when you start a search. The data that my search return contains prices for the same items bought in different days. I need the items in my listbox to be ordered ascending by price: as soon my BackgroundWorker returns items, those are asynchronously added in my listbox and ordered by price, letting me to see the best price as soon it is caught.

The better solution I found is having an ObservableCollection in my ViewModel and bind it to the ItemsSource of my ListBox I have a BackgroundWorker that starts the asynch search; I'm subscribed to a DataReceived EventHandler of the object that does the search and I notify the UI like this:

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

through a Dispatcher

private readonly Dispatcher _currentDispatcher;

This seems ok to me but doesn't order the items as I need so I found thet CollectionViewSource does exactly what I need in a simple way.

Here it is the problem:

If I set the datacontext of my listbox to the CollectionViewSource I have less design time power, I keep seeing my design time data in my listbox but I lose the DataContext in the Data tab of Blend.

So I did something that I think is a bit dirty: I named my ListBox with a x:Name attribute and added a bit of code behind in my MainWindow.xaml to swap the datasource of my named listbox at run time like this:

public MainWindow()
    {
        InitializeComponent();
        Closing += (s, e) => V开发者_如何转开发iewModelLocator.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
    }

Do you think it can be considered a sin?


You can bind the source of your collectionview to the observablecollecion. My question is: if you are using mvvm why are you doing all of this in what looks like codebehind?

ViewModel constructor:

 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...
                }

            }

I normally use a designtime service to create my Designtime Data: but in this case I just did a quick and dirty copy and paste:

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();
        }

I use ObservableCollections of ViewModels. OC's notification event only fires for items added or removed from the collection, by using a vm of the actual list item, you get change notification of the items properties if need be.

also you need to make sure you have your ViewModelLocator setup and defined right. I have found several posts now of people using MVVMLight but not using some of the most powerful features from it. If you use it as its designed to be used, it works like its supposed to work...I can attest to that.

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

I believe the IsDataSource attribute tells Blend to put it on the DataTab...But I dont use blend all that much for my Datamanipulations so I am not that worried about it.

0

精彩评论

暂无评论...
验证码 换一张
取 消