none
Binding e conferma/annulla in WinForms RRS feed

  • Domanda

  • Ciao a tutti,

    sono qui con una domandina da principiante.

    Nei miei progetti WinForms, a parte qualche raro caso, non ho mai utilizzato il Binding, ho sempre gestito tutto a mano.

    Prendiamo il caso di una classica classe "Persona" con due proprietà "Nome" e "Cognome" di tipo stringa.

    In una form mettiamo due TextBox, la txtNome e la txtCognome, collegate alle due proprietà. 

    Fino ad oggi, nel casi di WinForms, al caricamento di una istanza della classe Persona, sono sempre andato nel codice, ho passato alla txtNome.Text il valore di Persona.Nome e naturalmente la stessa cosa viene fatta per il cognome. Se poi l'utente va a modificare il nome o il cognome, alla pressione di un pulsante di conferma, e solo in quel momento, prendo il contenuto di txtNome.Text e lo passo alla proprietà Nome, così come il contenuto di txtCognome.text lo passo alla proprietà Cognome. Nel caso in cui l'operatore, dopo aver modificato qualcosa, preme il pulsante Annulla, nessuna proprietà dell'istanza di Persona viene cambiata.

    Cambiando approccio, volevo fare la stessa cosa con il Binding, quindi stessa classe e stessa form, apro le proprietà di txtNome, nella sezione (DataBindings) vado in "Text", creo una nuova origine di dati dando come origine la classe Persona, viene quindi creata la sorgente PersonaBindingSource. Scelgo quindi la proprietà Nome e chiudo.

    Stessa cosa con txtCognome, saltando naturalmente la creazione di una nuova sorgente, visto che già esiste.

    Caricando una istanza di Persona, e passando questa istanza come DataSource di PersonaBindingSource, le due textbox mostrano effettivamente i valori delle proprietà a loro associate.

    Ammetiamo che l'utente vada a modificare quanto scritto in txtNome; appena c'è la validazione del controllo, ad esempio mettendo il focus su un altro controllo, il contenuto di txtNome.Text viene "trasferito" alla proprietà, che dunque cambia valore.

    Se volessi annullare le modifiche? Come faccio ad avere di nuovo le proprietà di quella istanza di Persona come le avevo prima?

    C'è un modo per (una delle due) annullare le modifiche fatte oppure fare in modo che le proprietà dell'oggetto non vengano valorizzate finché non c'è una conferma?

    Sto usando, per la prima volta, Net 6, con VB2022

    Grazie e buona giornata

    Maurizio

       

    lunedì 4 aprile 2022 10:09

Risposte

  • Trovato!

    Per fare quello che intendevo, e cioè NON aggiornare i dati di un oggetto che funziona da fonte dati per il binding, se non su specifica richiesta (il tasto conferma, insomma), si può:

    Impostare la proprietà DataSourceUpdateMode dell'oggetto Binding associato al controllo, al valore "Never"

    Così facendo, il binding funziona a senso unico, cioè è in grado di leggere i dati dall'oggetto sorgente per "trasferirli" al controllo, ma non farà il percorso contrario, quindi ogni modifica che facciamo al controllo, banalmente al testo di una TextBox, non verrà riportata all'oggetto sorgente.

    Quando poi andiamo a premere il tasto di conferma, e quindi vogliamo che effettivamente le modifiche fatte sui controlli si riflettano sull'oggetto sorgente, dovremmo andare ad impostare, per il (o i, nel caso su un controllo ci fossero più oggetti Binding) Binding,, la proprietà DataSourceUpdateMode a "OnPropertyChanged", e poi per terminare l'opera, chiamare il metodo "EndEdit" dell'oggetto BindingSource.

    Se invece non vogliamo confermare le modifiche, non dobbiamo fare niente.

    Metto su due righe di codice tanto per spiegarmi meglio

    Come prima cosa, e questo l'ho fatto nel gestore di evento Load della form, ho impostato a Never la proprietà DataSourceUpdateMode dei vari oggetti Binding. Nel codice che vedete, ho dato per scontato che ogni controllo che ha il binding attivo, abbia un solo oggetto Binding, ma chiaramente se le esigenze sono diverse, basta poco per adattare

     Private Sub AssegnaNever()
    
            Dim lst = (From ctrl In Me.Controls.OfType(Of Control)
                       Where ctrl.DataBindings.Count = 1).ToList
    
            lst.ForEach(Sub(x) x.DataBindings(0).DataSourceUpdateMode = DataSourceUpdateMode.Never)
        End Sub

    Quando invece vado a chiedere il salvataggio delle modifiche fatte, tramite il mio metodo "Salva"

    Private Sub Salva()
            Dim lst = (From ctrl In Me.Controls.OfType(Of Control)
                       Where ctrl.DataBindings.Count = 1).ToList
            lst.ForEach(Sub(x) x.DataBindings(0).DataSourceUpdateMode = DataSourceUpdateMode.OnPropertyChanged)
    
            PersonaClassBindingSource.EndEdit()
        End Sub

    dove PersonaClassBindingSource è l'oggetto BindingSource creato sulla form. Naturalmente avrei potuto (e dovuto) evitare di replicare due volte il codice per l'assegnazione del valore voluto alla proprietà DatasourdceUpdateMode, in un contesto reale probabilmente avrei fatto un metodo unificato che, accettato come argomento il valore voluto, avrebbe fatto il resto. Così come, come dicevo prima, avrei dovuto fare in modo di non dare per scontato che la collection DataBindings non fosse popolata (al massimo) da un solo oggetto, ma direi che come dimostrativo, possa andare bene.
    martedì 10 maggio 2022 09:52

Tutte le risposte

  • Ciao Maurizio,

    Grazie per la descrizione dettagliata, sto seguendo anche io.


    Microsoft offre questo servizio gratuitamente, per aiutare gli utenti e aumentare il database dei prodotti e delle tecnologie. Il contenuto fornito “as is“ non comporta alcuna responsabilità da parte dell’azienda.

    lunedì 4 aprile 2022 11:59
    Moderatore
  • Grazie dell'interessamento, Plamen.

    Vediamo se qualcuno può almeno indirizzarmi.

    martedì 5 aprile 2022 07:28
  • Visto che non ho ancora risolto, provo a mettere lo stesso messaggio anche sul forum dedicato a C#, visto che non è una domanda strettamente legata al linguaggio.

    Se dovessi avere riscontro, naturalmente, aggiorno anche qui.

    giovedì 7 aprile 2022 13:40
  • Trovato!

    Per fare quello che intendevo, e cioè NON aggiornare i dati di un oggetto che funziona da fonte dati per il binding, se non su specifica richiesta (il tasto conferma, insomma), si può:

    Impostare la proprietà DataSourceUpdateMode dell'oggetto Binding associato al controllo, al valore "Never"

    Così facendo, il binding funziona a senso unico, cioè è in grado di leggere i dati dall'oggetto sorgente per "trasferirli" al controllo, ma non farà il percorso contrario, quindi ogni modifica che facciamo al controllo, banalmente al testo di una TextBox, non verrà riportata all'oggetto sorgente.

    Quando poi andiamo a premere il tasto di conferma, e quindi vogliamo che effettivamente le modifiche fatte sui controlli si riflettano sull'oggetto sorgente, dovremmo andare ad impostare, per il (o i, nel caso su un controllo ci fossero più oggetti Binding) Binding,, la proprietà DataSourceUpdateMode a "OnPropertyChanged", e poi per terminare l'opera, chiamare il metodo "EndEdit" dell'oggetto BindingSource.

    Se invece non vogliamo confermare le modifiche, non dobbiamo fare niente.

    Metto su due righe di codice tanto per spiegarmi meglio

    Come prima cosa, e questo l'ho fatto nel gestore di evento Load della form, ho impostato a Never la proprietà DataSourceUpdateMode dei vari oggetti Binding. Nel codice che vedete, ho dato per scontato che ogni controllo che ha il binding attivo, abbia un solo oggetto Binding, ma chiaramente se le esigenze sono diverse, basta poco per adattare

     Private Sub AssegnaNever()
    
            Dim lst = (From ctrl In Me.Controls.OfType(Of Control)
                       Where ctrl.DataBindings.Count = 1).ToList
    
            lst.ForEach(Sub(x) x.DataBindings(0).DataSourceUpdateMode = DataSourceUpdateMode.Never)
        End Sub

    Quando invece vado a chiedere il salvataggio delle modifiche fatte, tramite il mio metodo "Salva"

    Private Sub Salva()
            Dim lst = (From ctrl In Me.Controls.OfType(Of Control)
                       Where ctrl.DataBindings.Count = 1).ToList
            lst.ForEach(Sub(x) x.DataBindings(0).DataSourceUpdateMode = DataSourceUpdateMode.OnPropertyChanged)
    
            PersonaClassBindingSource.EndEdit()
        End Sub

    dove PersonaClassBindingSource è l'oggetto BindingSource creato sulla form. Naturalmente avrei potuto (e dovuto) evitare di replicare due volte il codice per l'assegnazione del valore voluto alla proprietà DatasourdceUpdateMode, in un contesto reale probabilmente avrei fatto un metodo unificato che, accettato come argomento il valore voluto, avrebbe fatto il resto. Così come, come dicevo prima, avrei dovuto fare in modo di non dare per scontato che la collection DataBindings non fosse popolata (al massimo) da un solo oggetto, ma direi che come dimostrativo, possa andare bene.
    martedì 10 maggio 2022 09:52
  • Ciao Maurzio,

    Grande come sempre! Grazie per aver spiegato come hai risolto in maniera così dettagliata.

    Segno i due thread come risolti.


    Microsoft offre questo servizio gratuitamente, per aiutare gli utenti e aumentare il database dei prodotti e delle tecnologie. Il contenuto fornito “as is“ non comporta alcuna responsabilità da parte dell’azienda.

    martedì 10 maggio 2022 11:31
    Moderatore
  • Segno i due thread come risolti.

    Grazie, Plamen!
    martedì 10 maggio 2022 12:38