none
ObservaleCollection Vs. IBindingList RRS feed

  • Domanda

  • Ciao a tutti,
    nel VM ho alcune ObservableCollection, modificate per consentire le operazioni CRUD sul DB, seguendo questo tutorial:

    https://blogs.msdn.microsoft.com/bethmassi/2009/05/08/using-the-wpf-observablecollection-with-ef-entities/

    Come model utilizzo EF collegato a DB SQLIte. Le classi POCO implementano INotifyPropertyChanged e IEditableObject. Tutto OK.
    Ho provato dalla view metodi come ".Add"con IEditableCollectionView: immediato e semplice.
    Ora vorrei avere le stesse opzioni ("IsAddingNew", etc.) all'interno del VM, così da rimanere in ambito MVVM.
    Che debba ricorrere alle CollectionView dal VM mi pare inopportuno.

    Allora ho trovato l'opzione delle IBindingList, ma ho letto che a livello prestazionale sono nettamente inferiori alle ObservableCollection, oltre alla complicata implementazione.

    E' possibile senza modificare le mie ObservableCollection? Soluzioni alternative, anche radicali?

    Grazie.


    Fabrizio Alessandri





    martedì 4 dicembre 2018 21:23

Tutte le risposte

  • Ciao Fabrizio,

    ho letto un paio di volte il tuo messaggio, ma lo trovo piuttosto criptico, riesci a spiegare con parole semplici, e qualche riga di codice, cosa fai e funziona e cosa vorresti fare e non riesci?

    Io ho un approccio un pochino all'antica sulla gestione dati, per cui la mia UI non sa nulla di chi gli fornisce I dati, però operazioni di modifica ai dati sul view model con opportuna chiamata per passare le modifiche al DB sono normali utilizzando un observable collection come contenitore per la lista delle entity che rappresentano il contenuto di una o più tabelle.

    saluti

    Sabrina


    Sabrina C. - http://www.dotnetwork.it

    giovedì 6 dicembre 2018 15:36
  • Ciao Sabrina,
    domando scusa se non son stato capace di essere chiaro.
    Ci riprovo :-)

    Dalla classe POCO "Customer", carico i records in una ObservableCollection (modificata come da link, per comunicare con il model) "Customers".
    Quest'ultima alimenta una ListBox che consente le operazioni CRUD sui singoli item.
    Se ho necessità di modificare un record, tramite un button apro una Form:

    In realtà è una grid nella MainWindow, che diventa visibile quando necessario.
    La grid è in binding con il SelectedItem della Listbox. La fase della modifica è gestita tramite IEditableObject, così da poter annullare l'operazione sull'item.

    Nel caso in cui voglia creare un nuovo record, faccio una insert nella collection, sempre tramite lo scenario utilizzato nella modifica. 
    Anche in questo caso vorrei poter gestire la transazione, in modo da poter annullare l'operazione sulla collection.

    Se utilizzassi un approccio misto, potei utilizzare dalla View IEditableCollectionView, che fornisce i metodi/proprietà appropriate e me la caverei con poche righe di codice. Invece, come precisato nel mio primo post, anche io desidero mantenere la separazione fra VM e View: MVVM.

    Nel frattempo, da quando ho pubblicato il thread, ho ricavato un'interfaccia (imita appunto la IEditableCollectionView) che ho estratto dalle mie ObservableCollection (una per tabella del DB) modificate e pare funzionare:

        public interface IEditableObservableCollection
        {
            bool CanAdd { get; }
            bool IsAdding { get; }
            ExampleEntities Context { get; }
    
            object AddNew();
            void CancelAddNew();
            void CommitAddNew();
            void Save();
            void Remove(int item);
        }

    La domanda è: il mio approccio è inutilmente farraginoso?

    Grazie.


    Fabrizio Alessandri


    giovedì 6 dicembre 2018 20:45
  • Potrebbe essere il giusto approccio, in base a quello che stai facendo quindi il suggerimento è, visto che hai trovato il modo per implementarlo fai un test su una tabella e se ti sembra funzionante continua.

    Se l'utente cambia idea dopo aver premuto l'ADD dovrebbe essere piuttosto semplice gestire un semplice flag sulla window della lista che in base al fatto che l'utente abbia premuto salva o annulla sia in grado di eliminare il record aggiunto.

    Non ho capito se la window dei dettagli è modale o meno, se fosse modale sarebbe semplice informare la window chiamante, se invece non fosse modale, puoi sollevare un evento dalla finestra di modifica ed intercettarlo nella finestra lista per sapere se eliminare il record o meno.

    anche in questo caso io sono una maniaca del controllo perciò implemento I pattern in modo molto semplice nel view model che supporta la window, certo è che in base al tipo di tabella lo stesso pattern lo posso implementare in modi diversi basati sul comportamento.

    Faccio un esempio, se ho una tabella semplice, con 2 o più campi, implemento un tipo di funzionalità per il CRUD ovvero l'approccio diretto con il pulsante salva e il pulsante undo.

    Se invece ho un oggetto complesso, che nel suo insieme è formato da molte tabelle, preferisco implementare un approccio di modifica "diretta" dei vari pezzettini che compongono l'oggetto automatizzando per buona parte il Salva e l'annulla.

    In ogni caso, su tutte le porzioni importanti dei dati, implemento un log delle modifiche molto dettagliato per sapere chi ha fatto cosa quando.

    saluti


    Sabrina C. - http://www.dotnetwork.it

    lunedì 10 dicembre 2018 11:09
  • <<Non ho capito se la window dei dettagli è modale o meno>>
    Non è modale, è una Grid invisibile di default.

    Faccio il punto.
    L'interfaccia andava bene, salvo alcuni problemi a perfezionare l'override del metodo "Remove".
    Poi però fra implementarla e a correggere alcune eccezioni, ho scritto decine di righe. Non mi piaceva.

    Sono tornato sui miei passi ed ho usato IEditableCollectionView:

        public class MainViewModel : ViewModelBase, IDisposable
        {
            private CollectionViewSource collectionViewSource = new CollectionViewSource();
            private IEditableCollectionView editableCollectionView;
    ...
    
            private void Execute_Add()
            {
                collectionViewSource.Source = ListBoxSource;
                editableCollectionView = (IEditableCollectionView)collectionViewSource.View;
    
                if (editableCollectionView.CanAddNew)
                {
                    SelectedItem = editableCollectionView.AddNew();
                }
            }
    Meno dell'impegno profuso nell'estrazione dell'interfaccia.

    In fondo rimango all'interno del VM e la View non sa niente del Model e viceversa.

    Troverei prezioso un tuo commento, almeno per i casi futuri.


    Fabrizio Alessandri



    martedì 11 dicembre 2018 19:15
  • Mi sembra sia ben fatto, 

    però probabilmente non conoscendo l'interfaccia che usi, e non vedendo tutto il codice domando, ma lo scopo non era implementare l'undo? o ricordo male io.

    Se l'implementazione dell'interfaccia ti permette un undo automatico direi che è ottima, se non lo fa, come lo implementi poi?

    solo per curiosità, perché io usualmente uso un approccio SDI quindi ho una window (modale o meno in base ai casi) per I dettagli e per implementare l'undo utilizzo un clone per le modifiche.

    Però è sempre bello imparare qualcosa di nuovo che posso trovare modo di applicare in futuro.


    Sabrina C. - http://www.dotnetwork.it

    mercoledì 12 dicembre 2018 12:28

  • Se l'implementazione dell'interfaccia ti permette un undo automatico direi che è ottima

    Sì, tutto automatico: l'implementazione è limitata alle poche righe che ho postato.
    L'interfaccia IEditableCollectionView ha metodi/proprietà ad hoc per gestire la transazione, comprensiva di undo.

    Grazie per i tuoi consigli Sabrina.


    Fabrizio Alessandri


    mercoledì 12 dicembre 2018 16:38