2010-03-11 18 views
8

Questa è l'applicazione MVV. Esiste una finestra e una classe del modello di viste correlate.Pulsante di attivazione in base al valore TextBox (WPF)

C'è TextBox, Button e ListBox nel modulo. Il pulsante è associato a DelegateCommand con funzione CanExecute. L'idea è che l'utente inserisca alcuni dati nella casella di testo, preme il pulsante e i dati vengono aggiunti alla casella di riepilogo.

Desidero attivare il comando (e il pulsante) quando l'utente immette i dati corretti in TextBox. Le cose funzionano in questo modo la società:

  • CanExecute() metodo contiene codice che controlla se i dati in immobili legati alla casella di testo è corretta.
  • Casella di testo è destinato ai beni in vista del modello
  • UpdateSourceTrigger è impostato su PropertyChanged e proprietà in vista del modello viene aggiornata dopo ogni utente preme chiave.

Il problema è che CanExecute() non si attiva quando l'utente immette i dati nella casella di testo. Non si attiva anche quando la casella di testo perde la messa a fuoco.

Come posso fare questo lavoro?

Edit:
commento di Re Yanko:
comando Delegato è implementato nel modello MVVM toolkit e quando si crea nuovo progetto MVVM, c'è il comando delegato in soluzione. Per quanto visto nei video Prism, questa dovrebbe essere la stessa classe (o almeno molto simile).

Ecco XAML frammento:

... 
    <UserControl.Resources> 
     <views:CommandReference x:Key="AddObjectCommandReference" 
           Command="{Binding AddObjectCommand}" /> 
    </UserControl.Resources> 

    ... 
    <TextBox Text="{Binding ObjectName, UpdateSourceTrigger=PropertyChanged}"> </TextBox> 
    <Button Command="{StaticResource AddObjectCommandReference}">Add</Button> 
    ... 

vista del modello:

// Property bound to textbox 
    public string ObjectName 
    { 
     get { return objectName; } 
     set { 
      objectName = value; 
      OnPropertyChanged("ObjectName"); 
     } 
    } 


    // Command bound to button 
    public ICommand AddObjectCommand 
    { 
     get 
     { 
      if (addObjectCommand == null) 
      { 
       addObjectCommand = new DelegateCommand(AddObject, CanAddObject); 
      } 
      return addObjectCommand; 
     } 
    } 

    private void AddObject() 
    { 
     if (ObjectName == null || ObjectName.Length == 0) 
      return; 
     objectNames.AddSourceFile(ObjectName); 
     OnPropertyChanged("ObjectNames"); // refresh listbox 
    } 

    private bool CanAddObject() 
    { 
     return ObjectName != null && ObjectName.Length > 0; 
    } 

come ho scritto nella prima parte della domanda, seguenti le cose funzionano: setter

  • immobili per ObjectName viene attivato su ogni pressione di un tasto nella casella di testo
  • se metto return true; in CanAddObject(), comando è attivo (pulsante a)

Mi sembra che il legame è corretto.

Cosa che non so è come rendere CanExecute() il fuoco nel setter della proprietà ObjectName dal codice precedente.

risposte di Abe di Re Ben e:

  • CanExecuteChanged() è gestore di eventi e compilatore si lamenta:

    L'evento 'System.Windows.Input.ICommand.CanExecuteChanged' possono essere visualizzati solo sul lato sinistro di + = o - =

  • ci sono solo altri due membri del ICommand: Execute() e CanExecute()

Avete qualche esempio, che mostra come posso effettuare una chiamata di comando CanExecute().

Ho trovato la classe helper del gestore comandi in DelegateCommand.cs e ci guarderò dentro, forse c'è qualche meccanismo che potrebbe aiutare.

In ogni caso, l'idea che per attivare il comando in base all'input dell'utente, sia necessario "nudge" l'oggetto comando nel codice di settaggio della proprietà appare maldestro. Introdurrà le dipendenze e uno dei grandi punti di MVVM li sta riducendo.

Edit 2:

ho cercato di attivare CanExecute chiamando addObjectCommand.RaiseCanExecuteChanged() a ObjectName proprietà setter dall'alto codice. Questo non aiuta neanche. CanExecute() viene attivato alcune volte quando il modulo viene inizializzato, ma successivamente non viene mai eseguito nuovamente. Questo è il codice:

// Property bound to textbox 
    public string ObjectName 
    { 
     get { return objectName; } 
     set { 
      objectName = value; 
      addObjectCommand.RaiseCanExecuteChanged();    
      OnPropertyChanged("ObjectName"); 
     } 
    } 

Edit 3: Soluzione

Come Yanko Yankov e JerKimball scritto, problema è risorsa statica. Quando ho cambiato pulsante vincolante come Yanko suggerito:

<Button Command="{Binding AddObjectCommand}">Add</Button> 

le cose hanno iniziato a lavorare immediatamente. Non ho nemmeno bisogno di RaiseCanExecuteChanged(). Ora CanExecute si attiva automaticamente.

Perché ho utilizzato la risorsa statica al primo posto?
Il codice originale era dal manuale WPF MVVM toolkit. L'esempio in quel manuale definisce i comandi come risorsa statica e quindi li associa alla voce di menu. La differenza è che al posto della proprietà stringa nel mio esempio, il manuale MVVM funziona con ObservableCollection.

Edit 4: spiegazione finale

ho finalmente capito. Tutto quello che dovevo fare era leggere il commento nella classe CommandReference. Dice:

/// <summary> 
/// This class facilitates associating a key binding in XAML markup to a command 
/// defined in a View Model by exposing a Command dependency property. 
/// The class derives from Freezable to work around a limitation in WPF when 
/// databinding from XAML. 
/// </summary> 

Quindi, CommandReference viene utilizzato per KeyBinding, non è per la rilegatura in elementi visivi. Nel codice precedente, i riferimenti ai comandi definiti nelle risorse funzionerebbero per KeyBinding, che non ho su questo controllo utente.
Ovviamente, il codice di esempio fornito con WPF MVVM toolkit era corretto, ma l'ho letto erroneamente e utilizzato CommandReference in associazione elementi visivi.
Questo MVVM WPF è davvero difficile a volte.

+0

In teoria questo dovrebbe funzionare. Dal momento che parli di DelegateCommand, dovremmo assumere che stai usando Prism/CAL o implementare ICommand da solo? Qualche codice sorgente sarebbe utile da guardare. Ad esempio, come si presenta l'associazione del pulsante (nel codice). Come si fa a collegare DelegateCommand, o se lo si implementa da soli, come si presenta l'implementazione, ecc. – irobot

risposta

4

Le cose sembrano molto più chiare ora con le modifiche, grazie! Questa potrebbe essere una domanda stupida (sono un po 'stanco di una lunga giornata di lavoro), ma perché non ti leghi direttamente al comando, invece che attraverso una risorsa statica?

<Button Command="{Binding AddObjectCommand}">Add</Button> 
+0

Anche questa è la mia prima impressione: dal momento che una risorsa statica non verrà continuamente rivalutata, persino chiamare RaiseCanExecuteChanged non farà granché. – JerKimball

+0

Sì, sembra che leghi il pulsante a un'istanza statica del modello di visualizzazione, che è un'istanza diversa da quella a cui è associata la casella di testo, e molto probabilmente che l'istanza precedente non ha alcuna connessione con il mondo esterno per cominciare . – irobot

+0

Avevi ragione. Il problema è con la risorsa statica. Si prega di vedere Edit3 per la spiegazione. – zendar

1

Provare a sollevare CanExecuteChanged quando la proprietà cambia.Il bind del comando è distinto dal binding della proprietà e i pulsanti associati ai comandi vengono segnalati a un cambiamento di stato dall'evento CanExecuteChanged.

Nel tuo caso, si potrebbe sparare un controllo quando si fa il PropertyChanged sulla proprietà bound che lo valuterà e impostare interno CanExecute la bandiera del comando e poi alzare CanExecuteChanged. Più di un "push" nell'oggetto ICommand di un "pull".

+0

Per favore, ricontrolla la domanda, ho aggiunto il codice e alcuni commenti riguardanti la tua risposta – zendar

3

Poiché si utilizza DelegateCommand, è possibile chiamare il metodo RaiseCanExecuteChanged quando la proprietà del testo cambia. Non sono sicuro di ciò che si sta cercando di realizzare con la vostra risorsa CommandReference, ma in genere è sufficiente associare i comandi direttamente alla proprietà Command dell'elemento tasto:

<TextBox Text="{Binding ObjectName, UpdateSourceTrigger=ValueChanged}" /> 
<Button Command="{Binding AddObjectCommand}" Content="Add" /> 

Questa sarebbe la quota di competenza del vostro modello di vista:

public string ObjectName 
{ 
    get { return objectName; } 
    set 
    { 
     if (value == objectName) return; 
     value = objectName; 
     AddObjectCommand.RaiseCanExecuteChanged(); 
     OnPropertyChanged("ObjectName"); 
    } 
} 
+0

Per favore, controlla di nuovo la domanda, ho aggiunto il codice e alcuni commenti riguardanti la tua risposta – zendar

+1

Abe! Pazzo DelegateCommand sparando pazzo! :) – JerKimball

1

Facendo eco Abe qui, ma il percorso "diritto" di fare qui è con:

public void RaiseCanExecuteChanged(); 

esposti su DelegateCommand. Per quanto riguarda le dipendenze, non penso che tu stia davvero facendo qualcosa di "cattivo" sollevandolo quando la proprietà che il comando dipende dalle modifiche all'interno di ViewModel. In tal caso, l'accoppiamento è più o meno contenuto interamente all'interno di ViewModel.

Quindi, prendendo il tuo esempio precedente, nel setter per "ObjectName", chiameresti RaiseCanExecuteChanged sul comando "AddObjectCommand".

+0

Jer, il tuo commento sulla risposta di Yanko era giusto. Si prega di vedere Edit3 per la spiegazione. – zendar

Problemi correlati