¿Por qué evitar el código en el patrón WPF MVVM?


En el artículo, Aplicaciones WPF Con El Patrón De Diseño Model-View-ViewModel , el autor que es Josh Smith dijo:

(1) En una arquitectura MVVM bien diseñada, el código posterior para la mayoría de las vistas debe estar vacío, o, a lo sumo, solo contener código que manipule los controles y recursos contenidos dentro de esa vista. (2) A veces también es necesario escribir código en el codebehind de una vista que interactúa con un objeto ViewModel, como conectar un evento o llamar a un método de lo contrario, sería muy difícil invocarlo desde el propio ViewModel.

Mi pregunta es, en el (1), por qué el código vacío detrás es considerado como un MVVM bien diseñado. (Suena que el código vacío detrás es siempre bueno.)

EDITAR: Mi pregunta es, como la siguiente, por qué el enfoque como el AttachedCommandBehavior o el InvokeCommandAction se intenta evitar la codificación codebehind.

Permítanme explicar más detalles.

En lo que respecta al (1), me gustaría piense como la siguiente situación a partir del AttachedCommandBehavior. Como el Borde no implementa el ICommandSource para el MouseRightButtonDown, comúnmente no se puede enlazar el evento y el ICommand, pero se puede hacer con el AttachedCommandBehavior.

<!-- I modified some code from the AttachedCommandBehavior to show more simply -->
<Border>
    <local:CommandBehaviorCollection.Behaviors>
           <local:BehaviorBinding Event="MouseRightButtonDown" 
                  Command="{Binding SomeCommand}" 
                  CommandParameter="A Command on MouseRightButtonDown"/>
    </local:CommandBehaviorCollection.Behaviors>
</Border>

O

Podemos hacer esto con el System.Windows.Interactivity.InvokeCommandAction.

<Border xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" >
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="MouseRightButtonDown">
            <i:InvokeCommandAction Command="{Binding SomeCommand}" 
               CommandParameter="A Command on MouseRightButtonDown"/>
        </i:EventTrigger>
    </i:Interaction.Triggers>
</Border>

PERO,

Utilizamos el siguiente XAML y su código posterior que tiene el método Border_MouseRightButtonDown, que está vinculado al (2) Josh Simth dijo arriba.

<Border MouseRightButtonDown ="Border_MouseRightButtonDown"/>

Creo que usar el codebehind como el anterior no es malo solo porque la diferencia entre estos es solo donde se enlaza un comando o add event handler.

¿Qué piensas de esto?

Author: Jin-Wook Chung, 2011-06-21

4 answers

Por qué el código vacío se considera un MVVM bien diseñado

Tener un fichero code-behind que consiste únicamente en una llamada a InitializeComponent() en su constructor significa que has alcanzado pureza - tienes absolutamente cero lógica en tu código. No ha contaminado su vista con ningún código que pertenezca legítimamente al viewmodel o modelo. Esto significa un par de cosas:

  • el viewmodel (y el model) es más fácil de probar de forma aislada
  • ha logrado un buen nivel de acoplamiento suelto, que tiene excelentes beneficios desde una perspectiva de mantenimiento y extensibilidad

Los beneficios realmente se notan cuando tiene que cambiar su interfaz de usuario, es decir, cambia de usar una ListView a una DataGrid, o cambia de usar los controles estándar de Microsoft a usar los de otros proveedores.

Como se mencionó, sin embargo, a veces es imposible evitar un poco de código en el archivo de código detrás. Lo que debe asegurarse es que el código que tiene está puramente relacionado con la interfaz de usuario. Como ejemplo, si tiene ComboA y ComboB, y ComboB se establece en respuesta a la selección en ComboA, entonces establecer el selectedIndex de ComboB desde la vista está bien, pero establecer los elementos o el SelectedItem de ComboB no lo está - esas propiedades están relacionadas con los datos y deben especificarse mediante el enlace al viewmodel. La propiedad selectedIndex está directamente relacionada visualmente y es algo independiente de los datos reales (y es irrelevante para the viewmodel).

Si accede al viewmodel desde el código detrás de la vista, debe intentar hacerlo a través de una interfaz. Esto significa que su viewmodel se inyecta o se da a la vista como una interfaz. (Tenga en cuenta que el subsistema de enlace no conoce ni se preocupa por la interfaz, continuará enlazando en su forma normal. Lo que esto logra es un mejor código, con un acoplamiento menos apretado). La forma en que lo codifico, el viewmodel no tiene idea de que existe una vista, y la vista solo conoce el viewmodel como interfaz.

Una cosa a recordar es que MVVM es un patrón, y un patrón es simplemente una receta o receta para lograr un cierto resultado en una determinada situación. No debe ser tratado como una religión, donde los no creyentes o inconformistas van a ir a algún purgatorio (aunque la adherencia al patrón es buena si quieres evitar el purgatorio de infierno de mantenimiento y olor a código).

Si quieres un excelente ejemplo de cómo ayuda este patrón en particular, intente escribir algunas pantallas razonablemente complicadas en ASP.Net y luego escribe lo mismo en WPF o Silverlight, y nota la diferencia.


Editar:

Permítanme responder algunas de sus preguntas, espero que ayude....

El rol de viewmodel (modelo de vista), en mi opinión, tiene lógica de interfaz de usuario y estado de una vista

El viewmodel nunca debe tener ninguna lógica de interfaz de usuario o "estado de vista" en él. A los efectos del presente explicación, definiría el estado de vista como posición de desplazamiento, índice de fila seleccionado, índice seleccionado, tamaño de ventana, etc. Ninguno de ellos pertenece al viewmodel; cosas como selectedIndex son específicas a la forma en que se muestran los datos en la interfaz de usuario (si cambia el orden de una cuadrícula de datos, entonces el selectedIndex puede cambiar, aunque el SelectedItem siga siendo el mismo). En este caso particular, el SelectedItem puede estar enlazado al viewmodel, pero el selectedIndex no debería.
Si necesitas hacer un seguimiento de la información de tipo de sesión de interfaz de usuario entonces usted debe llegar a algo genérico (por ejemplo, he persistido estado de vista antes por guardar cosas importantes en una lista KeyValuePair) que luego se "guarda" con una llamada a la viewmodel (a través de la interfaz que he mencionado anteriormente). La vista no tiene idea de cómo se guardan los datos, y el viewmodel no tiene idea de que los datos provienen de una vista (simplemente ha expuesto una llamada a través de su interfaz).

Y el rol de la vista está mostrando algunos contenido y sincronización del viewmodel (con código databinding)

Sí, la responsabilidad de la vista es simplemente mostrar visualmente los datos presentados por el viewmodel. El viewmodel obtiene los datos del modelo (el modelo es responsable de hacer llamadas a la base de datos o llamadas a WCF webservice, esto generalmente se hará a través de un "servicio", pero eso es otra discusión). El viewmodel puede entonces dar forma o manipular los datos, es decir, puede obtener una lista de todos los clientes, pero solo exponer un versión filtrada de esa lista (tal vez los clientes actuales) en una propiedad pública a la que la vista puede enlazar.
Si los datos se van a manipular en algo visual(un ejemplo común es un valor de enumeración que se traduce en un color), entonces el viewmodel todavía solo tiene el valor de enumeración, y la vista todavía se une a ese valor, pero la vista también utiliza un convertidor para traducir los datos puros a una representación visual. Al usar el convertidor, viewmodel aún ha evitado hacer cualquier cosa relacionada con la interfaz de usuario, y la vista ha evitado cualquier lógica real.

 55
Author: slugster,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-01-30 23:34:56

El MVVM puede dividir el código y el diseño de página por completo; los codificadores solo se preocupan por la codificación y los diseñadores solo se preocupan por el diseño. Pero:

  1. Nunca he visto a ningún diseñador que use Blend o understanding XAML.
  2. Casi todos los XAMLs están escritos por el propio codificador.
 6
Author: user4276887,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-11-21 03:16:05

No hay nada inherentemente malo en code-behind. Para casos simples, está bien tenerlo. Sin embargo, la lógica de la interfaz de usuario puede ser difícil de administrar en muchos escenarios. Encapsular esa lógica en comportamientos adjuntos y modelos de vista nos permite aislar las variables (y probarlas) para que sea más fácil de comprender y mantener.

Si la capacidad de prueba es una preocupación, cuanto más lógica de la interfaz de usuario pueda encapsular en viewmodels y comportamientos adjuntos, más podrá verificar sin recurrir a pruebas de interfaz de usuario. (Si bien no elimina la necesidad de realizar pruebas de interfaz de usuario por completo, proporciona un primer nivel de verificación antes de participar en las pruebas de interfaz de usuario, que requerirán más tiempo/recursos.

 4
Author: Michael Brown,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-06-21 07:07:33

Creo que la sección citada se refiere a la forma en que se visualizan los datos. Creo que significan que no debe escribir código en código detrás de eso, por ejemplo, relacionado con cómo o dónde se muestran los datos (por ejemplo, algo como: label1.Text = ...). Hacer cosas como esas usando enlaces hace que sea más fácil separar el diseño y el código (¿qué sucede si necesita que los datos se muestren en un cuadro de texto llamado "tbTest" en una versión posterior? Tendrías que cambiar tu código detrás).

No están diciendo que usted no debería tener ningún código en el código detrás-solo están diciendo que en un mundo ideal, solo reaccionaría a eventos o procesaría datos que de otra manera no podrían procesarse.

Al menos eso es lo que entiendo de la sección que citaste.

 2
Author: Thorsten Dittmar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-06-21 06:56:23