Pensieri sull'avere un viewmodel griglia generico e riutilizzabile

6

Sto sviluppando un'applicazione e io sono attualmente lottando per trovare il modo migliore per supportare uno scenario ricorrente in esso.

Questa app utilizza molte griglie. Ci sono un sacco di funzionalità che voglio includere nelle griglie, come il drag & rilascio, edizione di righe multiple, colorazione di righe condizionali, ecc. Ma allo stesso tempo, voglio che i miei oggetti dati derivino da una classe specifica orientata ai dati che non conosce alcun contesto di interfaccia utente e potrebbe essere utilizzata da un singolo modulo, con editor standalone.

Pertanto, mi piacerebbe avere una classe DataGridViewModel che esporrà a sua volta due raccolte: Rows come DataGridRowViewModel e Columns come DataGridColumnViewModel . Il modello di visualizzazione di riga esporrà una proprietà RowData che sarà l'oggetto di dati effettivo , mentre il modello di visualizzazione di colonna avrà un PropertyDescriptor (da System.ComponentModel spazio dei nomi) che indicherà quale oggetto dati campo per cui la colonna è destinata.

Puoi pensare a DataGridRowViewModel come contenitore per l'oggetto riga "reale".

L'obiettivo è di consentire la separazione delle preoccupazioni tra le funzionalità specifiche della griglia (controllo dell'altezza delle righe, del colore, della selezione, ecc.) e le funzionalità specifiche dei dati, in modo che non sia necessario conoscere l'altro. Idealmente, un terzo componente sarà a conoscenza di entrambi e garantirà una comunicazione corretta tra i due livelli (osservando un campo dati e modificando il colore della riga in condizioni x o y, ad esempio).

Questo approccio ha senso? Che tipo di complicazioni dovrei aspettarmi da questo (problemi vincolanti, prestazioni e simili)?

    
posta Crono 10.08.2015 - 19:41
fonte

3 risposte

2

Ho lavorato con le griglie per un po 'e ho avuto idee e scenari simili. C'è un difetto in questo approccio: vuoi molte funzionalità nella tua griglia, ma questo significa che avrai molti tipi di dati molto diversi. Quindi la tua griglia dovrà visualizzare e modificare tutti questi dati.

Questo non è un problema da solo, ma ciò significa che la griglia diventerà "Oggetto Dio" stesso. C'è un approccio diverso per risolvere questo problema: rendere la griglia un contenitore "sottile". Voglio dire non pensare a una griglia come a celle con testo.

Pensa a un layout.

Quindi:

  • Grid è davvero un elenco di righe. Ogni riga può avere celle, quindi dovrai creare un meccanismo per sincronizzare la larghezza delle colonne, ma alcune righe potrebbero essere speciali (raggruppamento, totali, dettagli, seprator, span ...)
  • Ogni cella è davvero un pannello per altri componenti: può essere piuttosto complessa, quindi non sarà solo testo al suo interno: può esserci una modifica con 2 pulsanti e un'immagine.
  • In questo modo griglia e riga non sanno cosa sia la cella, tranne che per le dimensioni e la posizione.
  • Tutto ciò significa che i dati di associazione alle celle non sono dati vincolanti per una matrice bidimensionale di valori, ma piuttosto un insieme di righe su una matrice di oggetti.
  • Le righe dovrebbero gestire le celle di associazione ai campi dell'oggetto o ad alcuni valori calcolati su alcuni campi .
  • Le celle devono passare i collegamenti ai componenti figlio
  • E i componenti all'interno delle celle (le texbox nei casi più semplici e più comuni) gestiranno infine l'associazione.

L'approccio che ho proposto è abbastanza complesso da implementare però. Forse non hai bisogno di casi così complessi. Inoltre, tieni presente che la presenza di questi numerosi componenti richiede ottimizzazioni (in particolare nei casi più semplici e più comuni). Ma nel risultato puoi avere una soluzione swiss-knife per tutti gli scenari con griglie.

Paragonato al tuo proposizionale ha alcuni vantaggi (e il tuo proposizionale ha dei limiti in queste aree): può prendere dati da campi profondi all'interno dei tuoi oggetti, può avere valori calcolati, non devi convertire i tuoi oggetti in " RowData @, può gestire diversi componenti affamati in una cella.

Per la tua domanda "Va davvero bene?" - sì. Ci sono molti grandi framework che tracciano le tue griglie, da ciò che mi viene in mente in questo momento: Qt hanno questo modo di legare. Quindi il tuo approccio è comunque una ben nota buona pratica con alcuni problemi e limiti noti.

    
risposta data 01.09.2017 - 21:30
fonte
0

Puntiamo a fare qualcosa di simile nel nostro progetto di interfaccia utente basato sul web, quindi quello che stai cercando di ottenere ha perfettamente senso per me. Il modello di visualizzazione dovrebbe essere piatto, quindi il binding dovrebbe essere semplice da raggiungere.

Nel nostro caso, abbiamo scoperto che stiamo usando liste di dati in tutto il nostro progetto, ma c'erano solo tre casi d'uso per l'elenco: "seleziona uno di questi elementi" (pulsante di selezione singola per riga), "seleziona uno o più di questi elementi "(caselle di controllo e pulsante di invio) e" gestisci questo elenco di elementi "(Aggiungi nuovo pulsante nella parte superiore, Modifica ed Elimina pulsanti per riga). In ogni caso, le colonne di dati sono di sola lettura.

Stiamo lavorando per sviluppare un componente con un costruttore che accetta un parametro per descrivere quale tipo di elenco è necessario e un oggetto modello di vista che ha 1-4 colonne di dati di visualizzazione più un ID come riferimento. Il componente capirà come dovrebbe apparire la lista da lì. Abbiamo creato una classe di annotazione personalizzata per decorare il modello di visualizzazione con:

[System.AttributeUsage(System.AttributeTargets.Property)]

public class ListViewColumn : System.Attribute
{
    private string _columnName;
    public int ColumnOrder;
    public string FormatString;

    public ListViewColumn(string columnName)
    {
        _columnName = columnName;
        ColumnOrder = 1;
        FormatString = null;
    }
}

Utilizzo:

    [ListViewColumn("Option Code", ColumnOrder = 1)]
    public string OptionCode { get; set; }

    [ListViewColumn("Version", ColumnOrder = 2)]
    public float Version { get; set; }

    [ListViewColumn("Description", ColumnOrder = 3)]
    public string Description { get; set; }

Se stavi creando un elenco per la metafora "Seleziona uno" (un attributo di classe può guidarlo), questo risulterebbe in un elenco simile al seguente:

        Option Code   Version   Description
Select  101-10G       4.2       Whatever this thing is
Select  234-567       4.35      A clear description of this other thing
Select  456-987       2.9       Something completely different

Utilizzare la stessa classe per generare un elenco "Gestisci" produrrebbe quanto segue:

                                                                 Add New
Option Code   Version   Description
101-10G       4.2       Whatever this thing is                   Edit | Delete
234-567       4.35      A clear description of this other thing  Edit | Delete
456-987       2.9       Something completely different           Edit | Delete
    
risposta data 10.08.2015 - 23:35
fonte
0

What kind of complications should I expect out of this

C'è una consistenza molto bella nel comportamento dell'interfaccia utente della nostra app che è profonda. E viene dai blocchi di base di cui GridView è uno. A questo livello fondamentale qualcuno ha fatto un ottimo lavoro con il design. Questi elementi costitutivi molto basilari sono la (e) base (e base!) Per le aree funzionali meno astratte, generali, di base.

Le chiavi di un insieme molto esoterico e utile di "lego" sono la progettazione dei costruttori di calcestruzzo e dell'addestramento del programmatore.

Devi allenare i tuoi programmatori su come lavorare con questi artefatti. Semplicemente non è ovvio come siano costruite le nostre astrazioni; sembra che tu inizi con una finestra con nient'altro che un paio di pulsanti, un generico gridview (senza un vero designer) e poi un big bang. Una finestra con 3 menu a discesa che popolano interattivamente 2 griglie con filtraggio, selezione e possibilmente interazione reciproca.

Il tuo codice costruttore deve essere ragionevolmente progettato; la più semplice applicazione OO è spesso sufficiente, ma quando i tuoi programmatori non hanno un apprezzamento del design esistente sei fregato. E onestamente la maggior parte dei programmatori con cui ho lavorato sono cattivi in OO. Spero che tu abbia recensioni sul codice. Più profondo è il codice nel processo di costruzione, più doloroso sono i suoi difetti.

Ad esempio abbiamo una classe che è metadata per la costruzione di gridviews in calcestruzzo. Non è altro che proprietà di stringa: un vero poster di DTO. Uno è un elenco CSV di nomi di colonne di tabelle DB (il primo è l'ordinamento predefinito, chi lo sapeva?), Un altro è i titoli di gridview, alcuni sono frammenti SQL. Alcuni valori sono quotati, altri no. Alcune citazioni singole, altre doppie. E dov'è il resto dell'SQL ?! Alcune proprietà sono state utilizzate insieme ma non sempre. Non ho capito nulla fino a quando ho letto molto codice e ho fatto un sacco di traccia del debugger. Quale idiota non sa a cosa serve un costruttore? Parametri del metodo? Una schifezza. Le classi client sono disseminate di codici procedurali che separano questi valori. Il cattivo e assente design OO si fa sentire lontano nello stack delle chiamate.

Ho finito per scrivere molti metodi di estensione per questa classe per l'utilizzo in nuove funzionalità. Con un sacco di test unitari! Non ho osato ri-scrivere la classe perché era in uso in circa 100 luoghi ed era abbastanza profonda nel processo di costruzione

    
risposta data 04.01.2017 - 16:13
fonte

Leggi altre domande sui tag