Esta es la aplicación MVVM. Hay una ventana y una clase de modelo de vista relacionada.Botón Habilitar basado en el valor del Cuadro de texto (WPF)
Hay TextBox
, Button
y ListBox
en el formulario. El botón está limitado a DelegateCommand
que tiene la función CanExecute
. La idea es que el usuario ingrese algunos datos en el cuadro de texto, presiona el botón y los datos se anexan al cuadro de lista.
Me gustaría habilitar el comando (y el botón) cuando el usuario ingresa los datos correctos en TextBox
. Las cosas funcionan así ahora:
CanExecute()
método contiene código que comprueba si los datos en la propiedad vinculada al cuadro de texto son correctos.- cuadro de texto está vinculado a la propiedad en vista del modelo
UpdateSourceTrigger
se establece enPropertyChanged
y la propiedad en vista del modelo se actualiza después de cada usuario presiona clave.
El problema es que CanExecute()
no se dispara cuando el usuario ingresa datos en el cuadro de texto. No se dispara incluso cuando el cuadro de texto pierde el foco.
¿Cómo podría hacer esto?
Editar:
comentario de Yanko Re:
comando Delegado se implementa en plantilla de conjunto de herramientas MVVM y cuando se crea nuevo proyecto MVVM, hay comandos Delegado en solución. Por mucho que vi en los videos de Prism, esta debería ser la misma clase (o al menos muy similar).
Aquí es fragmento de XAML:
...
<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>
...
Ver modelo:
// 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;
}
como he escrito en la primera parte de la pregunta, siguientes cosas funcionan: Organismo
- propiedad de
ObjectName
se activa en cada tecla presionada en el cuadro de texto - si pongo
return true;
enCanAddObject()
, comando está activo (botón a)
A mi me parece que la unión es correcta.
La cosa que no sé es cómo hacer CanExecute()
fuego en setter de ObjectName
propiedad del código anterior.
respuestas de Abe de Re: Ben y
CanExecuteChanged()
se controlador de eventos y el compilador se queja:el evento 'System.Windows.Input.ICommand.CanExecuteChanged' sólo puede aparecer en el lado izquierdo de + = o - =
sólo hay dos miembros más de
ICommand
:Execute()
yCanExecute()
¿Tiene algún ejemplo que muestra cómo puedo hacer una llamada al comando CanExecute()
.
Encontré la clase de ayudante del administrador de comandos en DelegateCommand.cs
y lo investigaré, tal vez haya algún mecanismo que pueda ayudar.
De todos modos, la idea de que para activar el comando en función de la entrada del usuario, es necesario "empujar" el objeto del comando en el código de configuración de la propiedad se ve torpe. Introducirá dependencias y uno de los grandes puntos de MVVM es reducirlas.
Edición 2:
me trató de activar CanExecute
llamando a addObjectCommand.RaiseCanExecuteChanged()
ObjectName
colocador propiedad desde arriba código. Esto tampoco ayuda. CanExecute()
se activa pocas veces cuando se inicializa el formulario, pero después de eso nunca se ejecuta de nuevo. Este es el código:
// Property bound to textbox
public string ObjectName
{
get { return objectName; }
set {
objectName = value;
addObjectCommand.RaiseCanExecuteChanged();
OnPropertyChanged("ObjectName");
}
}
Datos 3: Solución
Como Yanko Yankov y JerKimball escribió, es un problema recurso estático. Cuando cambié el enlace del botón como Yanko sugirió:
<Button Command="{Binding AddObjectCommand}">Add</Button>
cosas comenzaron a funcionar inmediatamente. Ni siquiera necesito RaiseCanExecuteChanged()
. Ahora CanExecute
se dispara automáticamente.
¿Por qué utilicé el recurso estático en primer lugar?
El código original era del manual WPF MVVM toolkit. El ejemplo de ese manual define los comandos como recurso estático y luego los vincula al elemento del menú. La diferencia es que en lugar de propiedad de cadena en mi ejemplo, el manual de MVVM funciona con ObservableCollection
.
Editar 4: explicación final
finalmente lo tengo. Todo lo que tenía que hacer era leer el comentario en la clase 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>
Así, CommandReference
se utiliza para KeyBinding
, no es para la unión de elementos visuales. En el código anterior, las referencias de comandos definidas en los recursos funcionarían para KeyBinding, que no tengo en este control de usuario.
Por supuesto, el código de muestra que venía con el kit de herramientas de WPF MVVM era correcto, pero lo leí mal y usé CommandReference
en el enlace de elementos visuales.
Este WPF MVVM realmente es complicado a veces.
En teoría, esto debería funcionar. Como mencionas DelegateCommand, ¿deberíamos asumir que estás usando Prism/CAL, o implementas ICommand tú mismo? Algún código fuente sería útil para mirar. Por ejemplo, cómo se ve el enlace del botón (en el código). ¿Cómo se conecta DelegateCommand, o si lo implementa usted mismo, qué aspecto tiene esa implementación, etc. – irobot