C# WPF MVVM & DataContext
Quote from Miche on 18/11/2020, 19:01Ciao a tutti,
da neofita sto sperimentando il pattern MVVM (con fatica), piano piano riesco ad avanzare ma ora sono un po' bloccato.
Ho una Window con tre TextBox e una ListView, tutte senza nome per tentare di implementare al meglio il pattern MVVM (da code behind sarebbe una passeggiata) nelle prime due leggo l'input e nella terza lo sommo, tanto per capire come funziona la cosa, poi ho una piccola classe che espone una lista caricata dal costruttore, in una listview.
Ho scritto meno codice possibile per concentrarmi sul binding .
Comunque, se imposto il datacontext su this, ovvero sulla MainWindow, il binding funziona correttamente con le TextBox, ma se voglio farlo funzionare con la ListView devo impostarlo su "utenti", che e' l'oggetto che espone la lista, ed e' istanziato anch'esso nella MainWindow, ma cosi' non sono piu' collegate le TextBox...
E' chiaro che mi sfugge qualcosa (magari fosse solo qualcosa), ma non capisco cosa.
Qualche idea? Allego il progetto nel caso qualcuno abbia la pazienza di guardalo 🙂
Mi basterebbe un indizio, tanto per capire in che direzione andare...
Ciao!!
Ciao a tutti,
da neofita sto sperimentando il pattern MVVM (con fatica), piano piano riesco ad avanzare ma ora sono un po' bloccato.
Ho una Window con tre TextBox e una ListView, tutte senza nome per tentare di implementare al meglio il pattern MVVM (da code behind sarebbe una passeggiata) nelle prime due leggo l'input e nella terza lo sommo, tanto per capire come funziona la cosa, poi ho una piccola classe che espone una lista caricata dal costruttore, in una listview.
Ho scritto meno codice possibile per concentrarmi sul binding .
Comunque, se imposto il datacontext su this, ovvero sulla MainWindow, il binding funziona correttamente con le TextBox, ma se voglio farlo funzionare con la ListView devo impostarlo su "utenti", che e' l'oggetto che espone la lista, ed e' istanziato anch'esso nella MainWindow, ma cosi' non sono piu' collegate le TextBox...
E' chiaro che mi sfugge qualcosa (magari fosse solo qualcosa), ma non capisco cosa.
Qualche idea? Allego il progetto nel caso qualcuno abbia la pazienza di guardalo 🙂
Mi basterebbe un indizio, tanto per capire in che direzione andare...
Ciao!!
Uploaded files:- You need to login to have access to uploads.
Quote from sabrina_c on 19/11/2020, 11:01Ciao Miche,
Se imposti il DataContext della window a this, tutte le property che espongono i tuoi dati devono essere dichiarate nella Window e devono implementare il PropertyChanged.
Non vedendo il tuo codice non riesco a capire bene perciò ti faccio delle domande:
- nella window hai istanziato la classe Utenti ed hai una property che la espone?
- La classe Utenti implementa l'interfaccia INotifyPropertyChanged?
- La tua window implementa l'interfaccia INotifyPropertyChanged?
- La lista che vuoi bindare alla Listview è una List oppure una ObservableCollection?
Ciao Miche,
Se imposti il DataContext della window a this, tutte le property che espongono i tuoi dati devono essere dichiarate nella Window e devono implementare il PropertyChanged.
Non vedendo il tuo codice non riesco a capire bene perciò ti faccio delle domande:
- nella window hai istanziato la classe Utenti ed hai una property che la espone?
- La classe Utenti implementa l'interfaccia INotifyPropertyChanged?
- La tua window implementa l'interfaccia INotifyPropertyChanged?
- La lista che vuoi bindare alla Listview è una List oppure una ObservableCollection?
Quote from Miche on 19/11/2020, 12:34Se imposti il DataContext della window a this, tutte le property che espongono i tuoi dati devono essere dichiarate nella Window e devono implementare il PropertyChanged.Intanto grazie per la disponibilita' e abbi pazienza perche' non ho le idee chiarissime, detto questo, se istanzio una classe nella Window e' corretto pensare che la sua property e' li dichiarata? Se si, ho fatto cosi', e sia la Window che la classe implementano il PropertyChanged.nella window hai istanziato la classe Utenti ed hai una property che la espone?
La classe Utenti implementa l'interfaccia INotifyPropertyChanged?
La tua window implementa l'interfaccia INotifyPropertyChanged?
La lista che vuoi bindare alla Listview è una List oppure una ObservableCollection?
Si, si,si ed e' una List, per ora volevo provare con questa anche se ho capito che un'ObservableCollection invierebbe automaticamente una notifica in caso di modifica di un elemento...
Approfittando della pazienza provo a mettere qui il codice, ho riguardato tutto, fatto un po' di prove, ma non riesco a trovare una soluzione, dovro' darmi all'ippica ^_^
Se imposti il DataContext della window a this, tutte le property che espongono i tuoi dati devono essere dichiarate nella Window e devono implementare il PropertyChanged.
nella window hai istanziato la classe Utenti ed hai una property che la espone?
La classe Utenti implementa l'interfaccia INotifyPropertyChanged?
La tua window implementa l'interfaccia INotifyPropertyChanged?
La lista che vuoi bindare alla Listview è una List oppure una ObservableCollection?
Si, si,si ed e' una List, per ora volevo provare con questa anche se ho capito che un'ObservableCollection invierebbe automaticamente una notifica in caso di modifica di un elemento...
Approfittando della pazienza provo a mettere qui il codice, ho riguardato tutto, fatto un po' di prove, ma non riesco a trovare una soluzione, dovro' darmi all'ippica ^_^
Quote from sabrina_c on 20/11/2020, 11:56Ho scaricato il tuo codice e gli do un occhiata appena ho qualche minuto così magari riesco a spiegarti meglio.
A presto
Ho scaricato il tuo codice e gli do un occhiata appena ho qualche minuto così magari riesco a spiegarti meglio.
A presto
Quote from Miky on 21/11/2020, 16:16Ciao, leggendo questo: private Utenti utenti = new Utenti(); ti direi di cambiare in public Utenti... Poi nello xaml dovrebbe bastare<ListView ItemsSource="{Binding Utenti.Lista, .... Un consiglio, creati una classe BaseNotifypublic class BaseNotify : INotifyPropertyChanged
{public virtual event PropertyChangedEventHandler PropertyChanged;
[DebuggerHidden()]
public virtual void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}Le altre tue classi erediteranno da essa
public class MyClass : BaseNotify
Poi ti basterà scrivere per ogni property della classe:
private string name;
public string Name
{
get { return name; }
set { name= value; NotifyPropertyChanged(); }
}non dovrai aggiungere altro e così il codice e' piu' leggibile e veloce da scrivere.
Ciao, leggendo questo: private Utenti utenti = new Utenti(); ti direi di cambiare in public Utenti... Poi nello xaml dovrebbe bastare
<ListView ItemsSource="{Binding Utenti.Lista, .... Un consiglio, creati una classe BaseNotify
public class BaseNotify : INotifyPropertyChanged
{
public virtual event PropertyChangedEventHandler PropertyChanged;
[DebuggerHidden()]
public virtual void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Le altre tue classi erediteranno da essa
public class MyClass : BaseNotify
Poi ti basterà scrivere per ogni property della classe:
private string name;
public string Name
{
get { return name; }
set { name= value; NotifyPropertyChanged(); }
}
non dovrai aggiungere altro e così il codice e' piu' leggibile e veloce da scrivere.
Quote from Miche on 21/11/2020, 20:18Ciao Miki,
private Utenti utenti = new Utenti(); ti direi di cambiare in public Utenti...
Ok, suppongo tu intenda utenti (l'istanza di Utenti), provato insieme a:
Poi nello xaml dovrebbe bastare ListView ItemsSource="{Binding Utenti.Lista, ....
Qui uguale, utenti.Lista, che e' una sintassi che avevo gia' provato (nei vari tentativi disperati :), ma non riesco ad ottenere il binding, che per inciso, funziona dichiarando utenti come DataContext...
Pero' mi stai facendo riflettere, ora devo capire cosa comporta definire private l'istanza di una classe public 🙂
Un consiglio, creati una classe BaseNotify.
.
non dovrai aggiungere altro e così il codice e' piu' leggibile e veloce da scrivere.
Ottimo consiglio, e grazie per l'aiuto!
Ciao Miki,
private Utenti utenti = new Utenti(); ti direi di cambiare in public Utenti...
Ok, suppongo tu intenda utenti (l'istanza di Utenti), provato insieme a:
Poi nello xaml dovrebbe bastare ListView ItemsSource="{Binding Utenti.Lista, ....
Qui uguale, utenti.Lista, che e' una sintassi che avevo gia' provato (nei vari tentativi disperati :), ma non riesco ad ottenere il binding, che per inciso, funziona dichiarando utenti come DataContext...
Pero' mi stai facendo riflettere, ora devo capire cosa comporta definire private l'istanza di una classe public 🙂
Un consiglio, creati una classe BaseNotify.
.
non dovrai aggiungere altro e così il codice e' piu' leggibile e veloce da scrivere.
Ottimo consiglio, e grazie per l'aiuto!
Quote from Miky on 22/11/2020, 20:23Quella che esponi come pubblica e' la property utenti (essendo pubblica la chiamerei Utenti).
Come già scritto da Sabrina, e' un requisito fondamentale per il binding (che sia pubblica, non che cominci con la "U" 🙂 ) .
public Utenti Utenti
{
get
{
return utenti;
}
set
{
if(utenti==value)return; //buona pratica per motivi lunghi da scrivere qui. Usalo sempre. Col tempo capirai quando non serve. Niente di complicato.
utenti = value;
NotifyPropertyChanged();
}
public MainWindow() { InitializeComponent(); Utenti = new Utenti(); //qui istanzi un nuovo oggetto Utenti che va a finire nella property pubblica Utenti DataContext = this; } Nello xaml ora il binding con Utenti funziona sicuramente e dal codice che ho visto funziona sicuramente anche Utenti.Lista. Se non dovesse funzionare per altri motivi, nella finestra di Output dovrebbe comparire qualche errore. Se dichiari Utenti come DataContex funziona uguale solo che devi mettere in binding solo Lista e non Utenti.Lista. public MainWindow() { InitializeComponent(); Utenti = new Utenti(); DataContext = Utenti; } Il DataContext vedilo come un object, gli puoi assegnare qualsiasi oggetto. Cosi' quando assegni un ItemSource puoi puntare ad una property di quell'oggetto. Quindi se scrivi DataContext = Utenti allora ItemSource sarà Lista DataContext = this (ovvero Window) ItemSource sarà Utenti.Lista perche' Utenti e' una property di this e Lista una property di Utenti. Comunque, in genere quando espongo delle liste ad una UI, preferisco usare delle ObservableCollection<Utente>, in quanto se un elemento della lista viene modificato, la cosa si riflette automaticamente nella UI. Cosa che non succede con le List.
Quella che esponi come pubblica e' la property utenti (essendo pubblica la chiamerei Utenti).
Come già scritto da Sabrina, e' un requisito fondamentale per il binding (che sia pubblica, non che cominci con la "U" 🙂 ) .
public Utenti Utenti
{
get
{
return utenti;
}
set
{
if(utenti==value)return; //buona pratica per motivi lunghi da scrivere qui. Usalo sempre. Col tempo capirai quando non serve. Niente di complicato.
utenti = value;
NotifyPropertyChanged();
}
public MainWindow() { InitializeComponent(); Utenti = new Utenti(); //qui istanzi un nuovo oggetto Utenti che va a finire nella property pubblica Utenti DataContext = this; } Nello xaml ora il binding con Utenti funziona sicuramente e dal codice che ho visto funziona sicuramente anche Utenti.Lista. Se non dovesse funzionare per altri motivi, nella finestra di Output dovrebbe comparire qualche errore. Se dichiari Utenti come DataContex funziona uguale solo che devi mettere in binding solo Lista e non Utenti.Lista. public MainWindow() { InitializeComponent(); Utenti = new Utenti(); DataContext = Utenti; } Il DataContext vedilo come un object, gli puoi assegnare qualsiasi oggetto. Cosi' quando assegni un ItemSource puoi puntare ad una property di quell'oggetto. Quindi se scrivi DataContext = Utenti allora ItemSource sarà Lista DataContext = this (ovvero Window) ItemSource sarà Utenti.Lista perche' Utenti e' una property di this e Lista una property di Utenti. Comunque, in genere quando espongo delle liste ad una UI, preferisco usare delle ObservableCollection<Utente>, in quanto se un elemento della lista viene modificato, la cosa si riflette automaticamente nella UI. Cosa che non succede con le List.
Quote from sabrina_c on 22/11/2020, 20:33Buongiorno Miche,
Ho dato un occhiata al tuo codice e te l'ho sistemato in modalità MVVM, ti listo un po' di cose in ordine sparso e ti invito a guardare il mio codice confrontarlo con il tuo e fare tutte le domande che ti vengono in mente sul perchè ho fatto le cose.
- Hai usato PropertyChanged come Namespace ma in realtà quello che dovevi fare era implementare l'interfaccia INotifyPropertyChanged, questa interfaccia si implementa aggiungendo la using System.ComponentModel ed implementando l'evento PropertyChanged nella classe. Questa interfaccia va implementata sia nella Window che nell'oggetto Utente.
- Il fatto che tu abbia chiamato PropertyChanged un Namespace significa che nessuno ti ha spiegato cosa sono i namespaces e perché si usano, se è così chiedi e provo a spiegarti. Nel mio esempio troverai che ho settato il RootNamespace sulle property del progetto e in tutte le classi sia in C# che Xaml nel modo in cui dovrebbe essere fatto, sia un progetto di studio, che una applicazione commerciale.
- Implementare le classi in file separati aiuta a non fare confusione.
- Le property della classe MainWindow sono il tuo ViewModel, devono essere implementate in modo da sollevare il PropertyChanged, per sollevare un evento, basta chiamare l'opportuno metodo implementato nella classe.
- La parte xaml era quasi a posto, fatto salvo che non ho capito il motivo di tante righe e colonne, fai attenzione che l'oggetto Grid di XAML ti serve per dividere lo spazio in cui mettere gli oggetti, mentre la ListView che hai usato crea da sola sia le righe che le colonne quindi basta predisporle un rettangolo nella Grid per e metterla al suo interno.
- Occhio che se vuoi che le TextBox siano modificabili nei due sensi (da codice o da utente) devi usare il Mode=TwoWay.
- Per dimostrarti l'uso Data Driven ho fatto in modo che alla modifica delle property in binding sulle Textbox la Textbox che ho aggiunto sotto alla ListView venga modificata automaticamente.
- Allo stesso modo, ho fatto in modo che se selezioni una riga della ListView il dato venga riportato sulla stessa textbox con opportuna descrizione.
- In questo caso ho fatto una chiamata nel Setter delle property, uno dei motivi per cui può essere utile l'implementazione verbosa delle property.
Adesso tocca a te con le domande.
Buongiorno Miche,
Ho dato un occhiata al tuo codice e te l'ho sistemato in modalità MVVM, ti listo un po' di cose in ordine sparso e ti invito a guardare il mio codice confrontarlo con il tuo e fare tutte le domande che ti vengono in mente sul perchè ho fatto le cose.
- Hai usato PropertyChanged come Namespace ma in realtà quello che dovevi fare era implementare l'interfaccia INotifyPropertyChanged, questa interfaccia si implementa aggiungendo la using System.ComponentModel ed implementando l'evento PropertyChanged nella classe. Questa interfaccia va implementata sia nella Window che nell'oggetto Utente.
- Il fatto che tu abbia chiamato PropertyChanged un Namespace significa che nessuno ti ha spiegato cosa sono i namespaces e perché si usano, se è così chiedi e provo a spiegarti. Nel mio esempio troverai che ho settato il RootNamespace sulle property del progetto e in tutte le classi sia in C# che Xaml nel modo in cui dovrebbe essere fatto, sia un progetto di studio, che una applicazione commerciale.
- Implementare le classi in file separati aiuta a non fare confusione.
- Le property della classe MainWindow sono il tuo ViewModel, devono essere implementate in modo da sollevare il PropertyChanged, per sollevare un evento, basta chiamare l'opportuno metodo implementato nella classe.
- La parte xaml era quasi a posto, fatto salvo che non ho capito il motivo di tante righe e colonne, fai attenzione che l'oggetto Grid di XAML ti serve per dividere lo spazio in cui mettere gli oggetti, mentre la ListView che hai usato crea da sola sia le righe che le colonne quindi basta predisporle un rettangolo nella Grid per e metterla al suo interno.
- Occhio che se vuoi che le TextBox siano modificabili nei due sensi (da codice o da utente) devi usare il Mode=TwoWay.
- Per dimostrarti l'uso Data Driven ho fatto in modo che alla modifica delle property in binding sulle Textbox la Textbox che ho aggiunto sotto alla ListView venga modificata automaticamente.
- Allo stesso modo, ho fatto in modo che se selezioni una riga della ListView il dato venga riportato sulla stessa textbox con opportuna descrizione.
- In questo caso ho fatto una chiamata nel Setter delle property, uno dei motivi per cui può essere utile l'implementazione verbosa delle property.
Adesso tocca a te con le domande.
Uploaded files:- You need to login to have access to uploads.
Quote from Miche on 23/11/2020, 01:31Quote from Miky on 22/11/2020, 20:23Quella che esponi come pubblica e' la property utenti (essendo pubblica la chiamerei Utenti).
.
.
Ciao Miky, ho letto tutto con interesse ed ora devo sperimentare un po' per capire bene, mi faccio vivo appena la nebbia si dirada, grazie!!
Quote from Miky on 22/11/2020, 20:23Quella che esponi come pubblica e' la property utenti (essendo pubblica la chiamerei Utenti).
.
.
Ciao Miky, ho letto tutto con interesse ed ora devo sperimentare un po' per capire bene, mi faccio vivo appena la nebbia si dirada, grazie!!
Quote from Miche on 23/11/2020, 01:36Quote from sabrina_c on 22/11/2020, 20:33Adesso tocca a te con le domande.
Ciao Sabrina, hai fatto un lavorone!
Ci sono parecchie cose che ora devo studiarmi, favoloso, mi faccio vivo appena sono riuscito a provare e ho qualcosa di intelligente da chiedere 🙂
Grazie!!
Quote from sabrina_c on 22/11/2020, 20:33Adesso tocca a te con le domande.
Ciao Sabrina, hai fatto un lavorone!
Ci sono parecchie cose che ora devo studiarmi, favoloso, mi faccio vivo appena sono riuscito a provare e ho qualcosa di intelligente da chiedere 🙂
Grazie!!