2009-12-12 15 views
35

tengo bastante simple (espero :)) problema:WPF: cómo señalar un evento desde ViewModel a View sin código en codebehind?

En MVVM, Vista general escucha en cambios de las propiedades del ViewModel. Sin embargo, a veces me gustaría escuchar el evento para que, por ejemplo, View pueda iniciar la animación o cerrar la ventana cuando las señales de VM.

Hacerlo a través de la propiedad bool con NotifyPropertyChanged (y comenzar la animación solo cuando cambia de falso a verdadero) es posible, pero se siente como un truco, prefiero exponer el evento, ya que es semánticamente correcto.

Además, me gustaría hacerlo sin código en código subyacente, ya que al hacer viewModel.myEvent += handler significaría que habría anulado el registro manualmente del evento para permitir que View sea GC'd - Las vistas de WPF ya son capaces de escucha las propiedades 'débilmente', y preferiría programar solo de manera declarativa en View.

La suscripción al evento fuerte estándar también es mala, porque necesito cambiar varios modelos de vista para una vista (porque la creación de la vista cada vez lleva demasiado tiempo de la CPU).

Gracias por las ideas (si hay una solución estándar, un enlace a msdn será suficiente)!

+1

El cambio de propiedad es un evento. ¿Qué no es semánticamente correcto al escuchar una propiedad bool en un gatillo? – adrianm

+10

Realmente no entiendo por qué las personas se oponen al código detrás de la vista. El motivo por el que las vistas responden a las propiedades se debe al código en el código subyacente, simplemente está oculto en el marco. –

+2

adrianm: Intenté crear una propiedad bool en la máquina virtual que siempre es falsa y generar OnPropertyChanged en ella, pero View no reaccionó; parece que WPF solo hará algo cuando cambie el valor real. Así que tendría que activar y desactivar ese bool, MyProp = true; OnPropertyChanged ("MiProp"); MyProp = falso; OnPropertyChanged ("MiProp"); - en lugar de RaiseMyEvent(). ¿Ver? –

Respuesta

2

Algunos comentarios:

  • Puede utilizar el weak event pattern para asegurar que la visión se puede GC'd incluso si todavía está unido a eventos del modelo de vista
  • Si ya está cambiando múltiples máquinas virtuales para una vista, ¿no sería ese el lugar ideal para adjuntar/separar el controlador?
  • Dependiendo de su escenario exacto, podría hacer que la VM exponga una propiedad de estado que la vista utiliza como activador de animaciones, transiciones y otros cambios visuales. Visual state manager es ideal para este tipo de cosas.
+0

Gracias por los enlaces, Kent. Sé sobre el patrón de eventos débiles, pero esperaba que WPF tuviera algo integrado (para vincular alguna acción con el evento VM) + los eventos débiles son realmente complicados en C#: http://www.codeproject.com/KB/cs/WeakEvents. aspx. La conmutación puede ocurrir desde varios lugares, y no puedo decir cuándo anular el registro del último oyente (esto se resolvería mediante eventos débiles). Estado es lo que no necesito - prácticamente tengo un estado, mis eventos no ocurren en los cambios de estado. Gracias, pero veré si alguien brinda una respuesta para mi caso. –

+0

Por cierto, me parece extraño que pueda activar, por ejemplo, la animación cuando la propiedad enlazada cambia de xa y, pero el escenario más natural (desencadenar animación cuando ocurre un evento) parece no ser compatible ... –

+0

El problema es que su el modelo solo tiene un estado ¿Por qué quieres comenzar una animación si nada cambia en el modelo? – adrianm

0

Como dijo adrianm, cuando activa su animación en una propiedad bool, en realidad está respondiendo a un evento. Específicamente el evento PropertyChanged que el subsistema WPF. Que está diseñado para conectarse/desconectarse correctamente de/para que no se pierda la memoria (puede olvidarse de hacer esto al cablear un evento usted mismo y causar una pérdida de memoria al tener una referencia activa a un objeto que de otra manera debería ser GC) .

Esto le permite exponer su ViewModel como DataContext para el control y responder correctamente al cambio de propiedades en el contexto de datos a través del enlace de datos.

MVVM es un modelo que funciona especialmente bien con WPF, porque de todas estas cosas que WPF le da, y el desencadenamiento de un cambio de propiedad es en realidad una manera elegante de utilizar todo el subsistema de WPF para lograr sus objetivos :)

+1

Hola Jim, gracias. También adoro MVVM, pero el problema es que cuando mi propiedad es falsa (o verdadera) todo el tiempo, el PropertyChanged ("MyPropertyName") no tiene ningún efecto: se llama al control wpf de escucha, pero nota que ningún parámetro realmente cambió, por lo que no hay ninguna razón para (por ejemplo) iniciar la animación. Tendría que hacer un hack feo como MyProp = true; PropertyChanged ("MyProp"); MyProp = falso; PropertyChanged ("MyProp"); para hacer que esto funcione, pero se siente torpe (y desperdicia algo de rendimiento también). –

0

Una pregunta más general es: "¿Por qué estoy tratando de lidiar con este evento en mi ViewModel?"

Si la respuesta tiene algo que ver con elementos de solo vista como animaciones, argumentaría que ViewModel no necesita saberlo: el código subyacente (cuando corresponda), Data/Event/Property Triggers y las construcciones más nuevas de VisualStateManager le servirá mucho mejor y mantendrá la separación limpia entre View y ViewModel.

Si algo tiene que suceder como resultado del evento, entonces lo que realmente quiere usar es un patrón de comando, ya sea usando el CommandManger, manejando el evento en el código e invocando el comando en el modelo de vista , o mediante el uso de comportamientos adjuntos en el sistema. Interactive libs.

De cualquier forma, desea mantener su ViewModel lo más "puro" que pueda: si ve algo específico de View allí, probablemente lo esté haciendo mal. :)

+4

No creo que haya entendido bien la pregunta, digamos que mi modelo es un cliente de Twitter que tiene el evento 'llegó un nuevo mensaje'. ViewModel está escuchando en Model y señala un evento 'NewMessageArrived' también (y no tiene idea de cómo reacciona View en este evento). Ahora, me gustaría que View inicie una animación cuando ocurre el evento. No hay cambio de estado (ver mis comentarios arriba), por lo que DataTrigger, PropertyTrigger y VisualStateManager están fuera de juego, y EventTrigger escucha en RoutedEvents, que se originan solo en UIElements (¡así que ViewModel no puede subirlos!). –

+0

Estoy totalmente de acuerdo con esto, si el código que necesita agregar SÓLO tiene que ver con la vista, es decir, las animaciones o la actualización de la vista, solo debería ser parte de la vista. Esto se debe a que si intenta crear otra vista, puede tener diferentes requisitos para las animaciones o la actualización. –

1

imho yYand separó

  1. estado - para ser capaz de mover datos de nuevo/hacia atrás entre vista < -> vm
  2. acciones - para ser capaz de llamar a las funciones de vista de modelo/comandos
  3. notificaciones - para poder señalar a la vista que algo ha sucedido y desea que tome una acción visual como hacer que un elemento brille, cambiar estilos, cambiar el diseño, enfocar otro elemento, etc.

si bien es cierto que puede hacer esto con un enlace de propiedad, es más un truco como mencionó Tomas; Siempre me ha parecido así.

mi solución para poder escuchar 'eventos' desde un modelo de vista también conocido como notificaciones es simplemente escuchar cambios en el contexto de datos y cuando cambia verifico que el tipo es el que estoy buscando y conecto el eventos. crudo pero simple.

Lo que realmente me gustaría es una forma simple de definir algunos desencadenantes de 'ver evento de modelo' y luego proporcionarle algún tipo de controlador que reaccione en el lado de la vista de todas las cosas en el xaml y solo coloque el código detrás para la materia thats no factible en XAML

2

Este hilo es bastante viejo, pero me va a proporcionar mi $ 0,02 porque es algo que he luchado con y recientemente ...

similar a lo que otros dicen , pero aquí hay un ejemplo con algunos fragmentos de código ... Este ejemplo muestra cómo usar pub/sub para tener una vista suscribirse a un evento activado por la VM, en este caso hago una GridView. Volver a vincular a asegurar la GV está en sincronía con la máquina virtual ...

Ver (Sub):

using Microsoft.Practices.Composite.Events; 
using Microsoft.Practices.Composite.Presentation.Events; 

private SubscriptionToken getRequiresRebindToken = null; 

    private void SubscribeToRequiresRebindEvents() 
    { 
     this.getRequiresRebindToken = 
      EventBus.Current.GetEvent<RequiresRebindEvent>() 
      .Subscribe(this.OnRequiresRebindEventReceived, 
       ThreadOption.PublisherThread, false, 
       MemoryLeakHelper.DummyPredicate); 
    } 

    public void OnRequiresRebindEventReceived(RequiresRebindEventPayload payload) 
    { 
     if (payload != null) 
     { 
      if (payload.RequiresRebind) 
      { 
       using (this.gridView.DeferRefresh()) 
       { 
        this.gridView.Rebind(); 
       } 
      } 
     } 
    } 

    private void UnsubscribeFromRequiresRebindEvents() 
    { 
     if (this.getRequiresRebindToken != null) 
     { 
      EventBus.Current.GetEvent<RequiresRebindEvent>() 
       .Unsubscribe(this.getRequiresRebindToken); 
      this.getRequiresRebindToken = null; 
     } 
    } 

sospechoso de llamadas del método se cierran para evitar pérdidas de memoria.

ViewModel (bar):

private void PublishRequiresRebindEvent() 
{ 
     var payload = new RequiresRebindEventPayload(); 
     payload.SetRequiresRebind(); 
     EventBus.Current.GetEvent<RequiresRebindEvent>().Publish(payload); 
} 

clase de carga útil

using System; 
using Microsoft.Practices.Composite.Presentation.Events; 

public class RequiresRebindEvent 
    : CompositePresentationEvent<RequiresRebindEventPayload> 
{ 

} 

public class RequiresRebindEventPayload 
{ 
    public RequiresRebindEventPayload() 
    { 
     this.RequiresRebind = false; 
    } 

    public bool RequiresRebind { get; private set; } 

    public void SetRequiresRebind() 
    { 
     this.RequiresRebind = true; 
    } 
} 

Tenga en cuenta que también puede establecer el constructor para permitir el paso de un GUID, o alguna identificado en que se puede configurar en Pub y se puede verificar en sub para asegurarse de que pub/sub esté sincronizado.

Cuestiones relacionadas