2011-10-05 9 views
5

Ho visto questo esempio - Binding.UpdateSourceTrigger PropertySet UpdateSourceTrigger per esplicita a ShowDialog (WPF MVVM)

nell'esempio l'UpdateSourceTrigger impostato su esplicita e poi nel codice della vista ha chiamata al UpdateSource del nome TextBox.

Ma se utilizzo MVVM dp non desidero avere nomi per i miei controlli e le proprietà di origine sono nella VM e non nella vista, quindi qual è il modo corretto per associare i controlli alle proprietà della VM e impostare UpdateSourceTrigger su esplicito ?

che voglio fare questo perché nel mio caso la sua ShowDialog finestra e voglio che la fonte si aggiornerà solo se l'utente fa clic su "OK"

Grazie in anticipo!

risposta

9

Se si sta utilizzando MVVM veramente quindi il pulsante OK scatto deve essere gestita da qualche Command. Questo comando deve provenire dal tuo ViewModel. Le proprietà associate a Expliticly devono provenire nuovamente dal tuo ViewModel. Quindi, cosa ti sta fermando.

  1. Non utilizzare Explicit vincolante, ma usano OneWay vincolante.
  2. In te pulsante, legare un comando e associare un parametro di comando alla proprietà OneWay dipendenza legato.
  3. Nel gestore Execute del comando (che deve essere un metodo del ViewModel), modificare la proprietà ViewModel con il parametro in arrivo.
  4. Sollevare il NotifyPropertyChanged per quella proprietà dal ViewModel.

Ad es

Si supponga che ho bisogno di aggiornare il testo di una TextBox nuovamente dentro il mio modello sul pulsante OK click.

Quindi per quello ho una classe EmployeeViewModel che contiene la proprietà EmployeeName. La proprietà è ha un getter e un setter. Il setter solleva la notifica modificata della proprietà. Il modello di vista ha anche un'altra proprietà di tipo ICommand denominata SaveNameCommand che restituisce un comando da eseguire.

EmployeeViewModel è il tipo di dati di contesto mio punto di vista. Myview ha un TextBox (chiamato come x: Name = "EmployeeNameTxBx") OneWay legato alla EmployeeName e un pulsante come OK. Leggo la proprietà Button.Command alla proprietà EmployeeViewModel.SaveNameCommand e Button.CommandParameter è legata alla proprietà EmployeeNameTxBx.Text.

 <StackPanel> 
      <TextBox x:Name="EmployeeNameTxBx" 
        Text="{Binding EmployeeName, Mode=OneWay}" /> 
      <Button Content="OK" 
        Command="{Binding SaveNameCommand}" 
        CommandParameter="{Bidning Text, ElementName=EmployeeNameTxBx}" /> 
     </StackPanel> 

Dentro il mio EmployeeViewModel devo OnSaveNameCommandExecute(object param) metodo per eseguire il mio SaveNameCommand.

In questo eseguire questo codice ...

 var text = (string)param; 
    this.EmployeeName = text; 

questo modo solo tasto OK clic, aggiorna il testo del TextBox nuovamente dentro EmployeeName proprietà del modello.

EDIT

Guardando i vostri commenti qui sotto, vedo che si sta cercando di implementare convalida su un'interfaccia utente. Ora questo cambia un po 'le cose.

IDataErrorInfo e relativi lavori di convalida SOLO SE i controlli di input (ad esempio TextBox) sono TwoWay associato. Sì, questo è come è destinato. Quindi ora puoi chiedere "Questo significa che l'intero concetto di NON CONCEDERE che i dati non validi da passare al modello sono futili in MVVM se usiamo IDataErrorInfo"?

Non proprio!

Vedere MVVM non applica una regola che SOLO i dati validi dovrebbero tornare. Accetta dati non validi ed è così che funziona IDataErrorInfo e vengono notificati gli errori. Il punto è che ViewModel è un semplice softcopy della visualizzazione in modo che possa essere sporco. Che cosa dovrebbe assicurarsi è che questa sporcizia non è impegnata per le interfacce esterne come servizi o database.

Tale flusso di dati non valido deve essere limitato dal ViewModel testando i dati non validi. E quei dati arriveranno se avremo il binding TwoWay abilitato. Quindi, considerando che stai implementando IDataErrorInfo, devi avere i binding TwoWay che sono perfettamente consentiti in MVVM.

Approccio 1:

Cosa succede se wan per validare in modo esplicito alcune voci sul interfaccia utente su pulsante di scatto?

Per questo utilizzare un trucco di convalida ritardata. Nel ViewModel viene visualizzato un flag chiamatoValidating. Impostalo come falso per impostazione predefinita.

nella vostra proprietà IDataErrorInfo.this saltare la convalida controllando bandiera isValidating ...

string IDataErrorInfo.this[string columnName] 
    { 
     get 
     { 
     if (!isValidating) return string.Empty; 

     string result = string.Empty; 
     bool value = false; 

     if (columnName == "EmployeeName") 
     { 
      if (string.IsNullOrEmpty(AccountType)) 
      { 
       result = "EmployeeName cannot be empty!"; 
       value = true; 
      } 
     } 
     return result; 
     } 
    } 

Poi nel comando OK eseguito gestore, controllare il nome del dipendente e poi alzare eventi di modifica di proprietà di notifica per la stessa proprietà ...

private void OnSaveNameCommandExecute(object param) 
    { 
     isValidating = true; 
     this.NotifyPropertyChanged("EmployeeName"); 
     isValidating = false; 
    } 

Questo attiva la convalida SOLO quando si fa clic su OK. Ricordare che lo EmployeeName DEVE contenere dati non validi affinché la convalida funzioni.

Approccio 2:

Cosa succede se voglio aggiornare esplicitamente attacchi senza modalità TwoWay in MVVM?

Quindi sarà necessario utilizzare Attached Behavior. Il comportamento si collegherà al pulsante OK e accetterà l'elenco di tutti gli elementi che necessitano di aggiornamento dei loro binding.

 <Button Content="OK"> 
      <local:SpecialBindingBehavior.DependentControls> 
       <MultiBinding Converter="{StaticResource ListMaker}"> 
        <Binding ElementName="EmployeeNameTxBx" /> 
        <Binding ElementName="EmployeeSalaryTxBx" /> 
        .... 
       <MultiBinding> 
      </local:SpecialBindingBehavior.DependentControls> 
     </Button> 

Il ListMaker è un IMultiValueConverter che converte semplicemente in un elenco valori ...

 Convert(object[] values, ...) 
     { 
      return values.ToList(); 
     } 

Nella tua SpecialBindingBehavior hanno un DependentControls proprietà modificata gestore ...

 private static void OnDependentControlsChanged(
      DependencyObject depObj, 
      DependencyPropertyChangedEventArgs e) 
     { 
      var button = sender as Button; 
      if (button != null && e.NewValue is IList) 
      { 
       button.Click 
        += new RoutedEventHandler(
         (object s, RoutedEventArgs args) => 
         { 
           foreach(var element in (IList)e.NewValue) 
           { 
           var bndExp 
            = ((TextBox)element).GetBindingExpression(
             ((TextBox)element).Textproperty); 

           bndExp.UpdateSource(); 
           } 
         }); 
      } 
     } 

Ma saranno ancora suggerire di usare la mia precedente MVVM puro basato ** Approccio 1.

+0

primo grazie per la tua risposta dettagliata! e tornando al mio problema, sto usando mvvm puro e ho il comando in my vm e sto usando NotifyPropertyChanged. ma se io uso OneWay binding e ho nella casella 10 textbox il suo significato ho bisogno di inviare i parametri di comando 10 nomi di elementi? e quindi aggiornarli manualmente? non c'è modo di usare il binding di ToWay? – Maya

+0

anche adesso sto usando IDataError per convalidare il testo nelle caselle di testo, se userò il binding OneWay posso ancora convalidare il testo? – Maya

+0

@Maya si prega di vedere il mio naswer modificato sopra. –

1

Questa è una vecchia domanda, ma voglio ancora fornire un approccio alternativo per gli altri utenti che incappano in questa domanda ... Nei miei viewmodels, non esporre direttamente le proprietà del modello nei metodi Property get/set. Io uso variabili interne per tutte le proprietà. Quindi lego tutte le proprietà a due vie. Quindi posso fare tutta la validazione come "normale" perché solo le variabili interne sono cambiate. Nella vista costruttore di modelli, ho l'oggetto modello come parametro e imposto le variabili interne ai valori del mio modello. Ora quando clicco sul pulsante "Salva" (-> Salva l'attivazione del comando nel mio modello di visualizzazione attiva) e non ci sono errori, ho impostato tutte le proprietà del mio modello sui valori della variabile interna corrispondente. Se clicco su "Canel/Undo" -Button (-> Cancel-Command nel mio modello di visualizzazione attiva), imposto le variabili interne ai valori del mio modello non modificato (usando i setter delle proprietà del modello di vista in modo che NotifyPropertyChanged sia chiamato e la vista mostra i cambiamenti = vecchi valori).

Un altro approccio potrebbe essere implementare Memento-Support nel modello, quindi prima di iniziare a modificare chiamate una funzione nel modello per salvare i valori correnti e, se si annulla la modifica, si chiama una funzione per ripristinare tali valori. .. in questo modo avresti annullato/annullato il supporto ovunque non solo in un modello di visualizzazione ... Ho implementato entrambi i metodi in diversi progetti ed entrambi funzionano bene, dipende dai requisiti del progetto ...