2010-03-11 19 views
8

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 en PropertyChanged 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; en CanAddObject(), 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() y CanExecute()

¿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.

+0

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

Respuesta

4

Las cosas se ven mucho más claras ahora con las ediciones, ¡gracias! Esta podría ser una pregunta estúpida (estoy un poco cansado de un largo día de trabajo), pero ¿por qué no vincular el comando directamente, en lugar de a través de un recurso estático?

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

Esta es también mi primera corazonada: dado que un recurso estático no se volverá a evaluar continuamente, ni siquiera llamar a RaiseCanExecuteChanged hará mucho. – JerKimball

+0

Sí, parece que está vinculando el botón a una instancia estática del modelo de vista, que es una instancia diferente de la que está vinculado el cuadro de texto, y es muy probable que la instancia anterior carezca de conexión con el mundo exterior para comenzar . – irobot

+0

Tenías razón. El problema es con el recurso estático. Por favor, mira Edit3 para una explicación. – zendar

1

Intente aumentar CanExecuteChanged cuando su propiedad cambie.El enlace de comando es realmente distinto del enlace de propiedad y los botones vinculados a comandos son alertados de un cambio de estado por el evento CanExecuteChanged.

En su caso, puede disparar un cheque cuando hace el PropertyChanged en la propiedad enlazada que lo evaluaría y establecer el indicador CanExecute interno del comando y luego aumentar CanExecuteChanged. Más de un "empujón" en el objeto ICommand que un "tirón".

+0

Por favor, consulte la pregunta nuevamente, agregué código y algunos comentarios con respecto a su respuesta – zendar

3

Dado que está utilizando DelegateCommand, puede llamarlo al método RaiseCanExecuteChanged cuando cambia la propiedad del texto. No estoy seguro de lo que está tratando de lograr con su recurso CommandReference, pero por lo general que acaba de vincular los comandos directamente al Comando de la propiedad del elemento de botón:

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

Esta sería la parte relevante de su modelo de vista:

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

Por favor, vuelva a consultar la pregunta, agregué el código y algunos comentarios con respecto a su respuesta – zendar

+1

Abe! ¡Eres un delegado tonto delegado! :) – JerKimball

1

Haciendo eco de Abe aquí, pero el camino "correcto" para tomar aquí está usando:

public void RaiseCanExecuteChanged(); 

expuestos en DelegateCommand. En cuanto a las dependencias, no creo que realmente esté haciendo algo "malo" al plantear esto cuando la propiedad de que el comando depende de los cambios dentro de ViewModel. En ese caso, el acoplamiento está más o menos contenido por completo dentro del ViewModel.

Por lo tanto, tomando su ejemplo anterior, en su setter para "ObjectName", llamaría a RaiseCanExecuteChanged en el comando "AddObjectCommand".

+0

Jer, tu comentario sobre la respuesta de Yanko fue correcto. Por favor, mira Edit3 para una explicación. – zendar

Cuestiones relacionadas