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 suPropertyChanged
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;
inCanAddObject()
, 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()
eCanExecute()
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.
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