INotifyPropertyChanged vs DependencyProperty en ViewModel

353

Al implementar ViewModel en una aplicación WPF de arquitectura Model-View-ViewModel, parece haber dos opciones principales sobre cómo hacer que se puedan combinar datos. He visto implementaciones que usan DependencyPropertypara propiedades con las que la Vista se va a unir y he visto la implementación de ViewModel en su INotifyPropertyChangedlugar.

Mi pregunta es ¿cuándo debería preferir uno sobre el otro? ¿Hay alguna diferencia de rendimiento? ¿Es realmente una buena idea dar las dependencias de ViewModel a WPF? ¿Qué más debo tener en cuenta al tomar la decisión de diseño?

bitbonk
fuente
11
consulte stackoverflow.com/questions/1329138/… para ver una forma comprobada del compilador de implementar INotifyPropertyChanged. Evitar tener los nombres de propiedad como una cadena mágica.
Ian Ringrose el
10
En general, existe una diferencia importante entre una propiedad de dependencia y una propiedad normal en una clase que implementa INotifyPropertyChanged. Las propiedades de dependencia pueden ser fuente o destino en el enlace de datos, pero las propiedades normales con soporte INotifyPropertyChanged solo se pueden usar como fuente. Entonces estas soluciones no son completamente intercambiables. La infraestructura de enlace de datos requiere un DP como destino para funcionar, pero el origen puede ser una propiedad normal con soporte INotifyPropertyChanged o un DP común.
Mostafa Rezaei
44
Consulte stackoverflow.com/a/10595688/200442 para conocer la forma de implementación de .net 4.5 INotifyPropertyChanged.
Daniel Little
mejor explicado aquí stackoverflow.com/a/3552550/366064
Bizhan

Respuestas:

214

Kent escribió un blog interesante sobre este tema: Ver modelos: POCOs versus DependencyObjects .

Breve resumen:

  1. Dependencia Los objetos no están marcados como serializables
  2. La clase DependencyObject anula y sella los métodos Equals () y GetHashCode ()
  3. Un DependencyObject tiene afinidad de hilo: solo se puede acceder en el hilo en el que se creó

Prefiero el enfoque POCO. Puede encontrar una clase base para PresentationModel (también conocido como ViewModel) que implementa la interfaz INotifyPropertyChanged aquí: http://compositeextensions.codeplex.com

jbe
fuente
24
DependencyObject también depende de las bibliotecas de WPF, mientras que POCO no, lo que permite que sus modelos de vista controlen alguna otra pila de IU donde WPF no está disponible (Compact Framework, Mono).
codekaizen
26
Está claro, entonces, que las Propiedades de dependencia se crean únicamente para la interfaz de usuario y no para la capa empresarial.
Andrei Rînea
11
Las propiedades de dependencia también requieren un padre DependencyObject. Su ViewModel realmente no debería heredar de DependencyObject.
Gusdor
38

Según la guía de rendimiento de WPF, los DependencyObjects definitivamente funcionan mejor que los POCO que implementan INotifyPropertyChanged:

http://msdn.microsoft.com/en-us/library/bb613546.aspx

James Ashley
fuente
1
Debo estar de acuerdo con eso ;-): blog.lexique-du-net.com/index.php?post/2010/02/24/…
Jonatha ANTOINE
Si selecciona .NET Framework versión 4, entonces el enlace aún funciona. Simplemente no está disponible para la "versión actual".
doubleYou
Gracias por señalar esto, hay mucha información errónea escandalosa por parte de los desarrolladores que hacen declaraciones engañosas de que INotifyPropertyChanged es más rápido o incurre en menos gastos generales que los DP y simplemente es infundado. Los DP son formas rápidas, elegantes y potentes de definir estructuralmente el árbol (de datos) virtual.
tpartee
Hay un mal oculto en los Objetos de dependencia. Deben crearse en el mismo hilo que los controles que se unen a ellos. Eso significa hilo GUI. Eso significa que debe enviar la creación a ese hilo. No puede tener esas cosas cargadas y creadas en algún hilo de fondo de DB, por ejemplo. A menos que despaches la creación. Insano.
ed22
28

La elección se basa totalmente en la lógica de su negocio y el nivel de abstracción de la interfaz de usuario. Si no desea una buena separación, DP trabajará para usted.

DependencyProperties se aplicará principalmente en el nivel de VisualElements, por lo que no será una buena idea si creamos muchos DP para cada uno de nuestros requisitos comerciales. También hay un mayor costo para DP que un INotifyPropertyChanged. Cuando diseñe un WPF / Silverlight, intente diseñar la interfaz de usuario y ViewModel totalmente separados para que en cualquier momento podamos cambiar los controles de diseño e interfaz de usuario (según el tema y los estilos)

Consulte esta publicación también: /programming/275098/what-applications-could-i-study-to-understand-datamodel-view-viewmodel . El enlace tiene mucha referencia al patrón Model-View-ViewModel, que es muy relevante para esta discusión.

Jobi Joy
fuente
99
La publicación de jbe responde a las diferencias con mayor precisión. El hecho de que una VM (o Presentador) herede de DependencyObject no significa que no se pueda diseñar o que no esté lógicamente separado de la Vista, solo significa que el almacenamiento de los valores de propiedad es diferente de los campos declarados explícitamente en Estilo POCO. Dicho esto, la serialización, la igualdad lógica y la afinidad de subprocesos son problemas reales que las VM basadas en DepedencyObject tienen que tratar.
micahtan
"También hay un mayor costo para DP que un INotifyPropertyChanged" - ¿Dónde está su fuente de prueba sobre esto? Muchos desarrolladores hacen este reclamo sin ninguna evidencia que lo respalde. Según MSDN, no es cierto. "Intente diseñar la interfaz de usuario y ViewModel totalmente separados para que en cualquier momento podamos cambiar los controles de diseño y de interfaz de usuario". De nuevo, esto no tiene absolutamente nada que ver con POCO + PropChange versus DO / DP. En todo caso, el registro Reflection and Path en DO / DP mejora su capacidad de trabajar en el lado visual.
tpartee
20

Desde el punto de vista de la expresividad, disfruto mucho el uso de las propiedades de dependencia y me encojo al pensar en ello INotifyPropertyChanged. Además de los stringnombres de propiedad y las posibles pérdidas de memoria debido a la suscripción de eventos, INotifyPropertyChangedes un mecanismo mucho más explícito.

Las propiedades de dependencia implican "cuando esto, haga eso" utilizando metadatos estáticos fáciles de entender. Es un enfoque declarativo que consigue mi voto por la elegancia.

Bryan Watts
fuente
1
La parte de la cadena ahora tiene una solución con el operador nameof.
Newtopian
@Newtopian: Cierto. También hay algunas cosas interesantes posibles con [CallerMemberName].
Bryan Watts
Sin mencionar la gran cantidad de beneficios de Registro de Propiedad (Reflexión) en WPF y CLR cuando se usa un modelo DO / DP versus un POCO.
tpartee
16

INotifyPropertyChanged cuando se usa también le brinda la capacidad de agregar más lógica en el código de sus captadores y definidor de sus propiedades.

DependencyProperty ejemplo:

public static DependencyProperty NameProperty = DependencyProperty.Register( "Name", typeof( String), typeof( Customer ) );

public String Name
{
    set { SetValue( NameProperty, value ); }
    get { return ( String ) GetValue( NameProperty ); }
}

En su getter y setter --- todo lo que puede hacer es simplemente llamar a SetValue y GetValue respectivamente, b / c en otras partes del marco no se llama al getter / setter, sino que llama directamente a SetValue, GetValue, por lo que su lógica de propiedad no ser ejecutado de manera confiable.

Con INotifyPropertyChanged, defina un evento:

public event PropertyChangedEventHandler PropertyChanged;

Y luego simplemente tenga cualquier lógica en cualquier parte de su código, luego llame:

// ...
// Something cool...
// ...

if( this.PropertyChanged != null )
{
    PropertyChanged( this, new PropertyChangedEventArgs( "Name" ) );
}

// More cool stuff that will reliably happen...

Esto podría estar en un getter / setter, o en cualquier otro lugar.

Adán
fuente
11
También puede obtener notificaciones de cambio de DependencyProperties. Ver PropertyMetadata.PropertyChangedCallback. Ejemplo en: msdn.microsoft.com/en-us/library/ms745795.aspx
Joe White el
2
Además, también puede llamar a SetValue desde cualquier lugar, no solo desde el interior de la propiedad
aL3891,
Esto es engañoso y falso: hay varias formas de conectarse a eventos de cambio en un DP, incluso cuando se cambia 'internamente'. Uno de ellos fue señalado anteriormente por Joe White
tpartee el
16

Las propiedades de dependencia están destinadas a admitir enlaces (como destino) en elementos de la interfaz de usuario, no como fuente de enlace de datos, aquí es donde entra INotifyProperty. Desde un punto de vista puro, no debe usar DP en un ViewModels.

"Para ser la fuente de un enlace, una propiedad no necesita ser una propiedad de dependencia; puede usar cualquier propiedad CLR como fuente de enlace. Sin embargo, para ser el objetivo de un enlace, la propiedad debe ser un propiedad de dependencia. Para que un enlace unidireccional o bidireccional sea efectivo, la propiedad fuente debe admitir notificaciones de cambio que se propaguen al sistema de enlace y, por lo tanto, al destino. Para fuentes de enlace CLR personalizadas, esto significa que la propiedad debe admitir INotifyPropertyChanged. Las colecciones deben admitir INotifyCollectionChanged ".

Todos los objetos de dependencia no se pueden serializar (esto podría dificultar el uso de ViewModels y DTO (POCO).

Hay diferencias entre DP dentro de Silverlight en comparación con WPF.

http://msdn.microsoft.com/en-us/library/cc221408(v=VS.95).aspx

http://msdn.microsoft.com/en-us/library/cc903933(VS.95).aspx

Nick Hermans
fuente
He estado usando objetos de dependencia serializados desde 2009 sin problemas, así que no estoy seguro de lo que estás hablando cuando dices "Todos los objetos de dependencia no se pueden serializar", sí que pueden. De hecho, hay muchas opciones: codeproject.com/Articles/61440/… emphess.net/2008/11/25/dependencyproperty-serialization y uno de mis favoritos personales: simplemente proporcionar almacenes de respaldo para todos sus DP y hacerlos serializables ( no hay buenos ejemplos simples disponibles en 2 minutos de búsqueda en Google, pero le aseguro que esto funciona).
tpartee
7

Yo también tuve que considerar esta decisión recientemente.

Descubrí que el mecanismo INotifyPropertyChanged se adaptaba mejor a mis necesidades porque me permitía pegar mi GUI a un marco lógico comercial existente sin duplicar el estado. El marco que estaba usando tenía su propio patrón de observación y era fácil reenviar un nivel de notificación al siguiente. Simplemente tuve una clase que implementó la interfaz de observador desde mi marco de lógica de negocios y la interfaz INotifyPropertyChanged.

Con DP no puede definir el backend que almacena el estado usted mismo. Hubiera tenido que dejar que .net almacenara en caché una copia de cada elemento de estado al que estaba vinculado. Esto parecía una sobrecarga innecesaria: mi estado es grande y complicado.

Entonces, aquí encontré INotifyPropertyChanged mejor para exponer propiedades de la lógica empresarial a la GUI.

Dicho esto, donde necesitaba un widget GUI personalizado para exponer una propiedad y para que los cambios en esa propiedad afecten a otros widgets GUI DP demostró la solución simple.

Entonces allí encontré DP útil para la notificación de GUI a GUI.

morechilli
fuente
6

¿Es realmente una buena idea dar las dependencias de ViewModel a WPF?

.NET 4.0 tendrá System.Xaml.dll, por lo que no tendrá que depender de un marco arbitrario para utilizarlo. Vea la publicación de Rob Relyea sobre su sesión de PDC.

Mi toma

XAML es un lenguaje para describir objetos, y WPF es un marco cuyos objetos descritos son elementos de la interfaz de usuario.

Su relación es similar a C #, un lenguaje para describir la lógica, y .NET, un marco que implementa tipos particulares de lógica.

El propósito de XAML son los gráficos de objetos declarativos. Las tecnologías W * F son excelentes candidatos para este paradigma, pero XAML existe independientemente de ellas.

XAML y todo el sistema de dependencia se implementaron como pilas separadas para WF y WPF, probablemente para aprovechar la experiencia de diferentes equipos sin crear una dependencia (sin juego de palabras) entre ellos.

Bryan Watts
fuente
Al responder, parece estar asumiendo que Bitbonk considera que XAML y WPF son lo mismo. ViewModels debería tener la menor dependencia de WPF posible, no para aumentar la separación lógica, sino para disminuir la complejidad del código y evitar todos los problemas asociados con simplemente escribir la lógica en el código subyacente de un control de usuario. Inevitablemente implementará conceptos de WPF como ICommand y presentará un comportamiento que solo WPF / Silverlight podrá ajustar fácilmente; su única inquietud de presentación en un modelo de vista debería ser CollectionViews y ObservableCollection.
Gusdor
6

Las propiedades de dependencia son el pegamento de la creación de controles personalizados. Si está interesado en usar Intelli-sense para mostrar sus propiedades en la ventana de propiedades en tiempo de diseño XAML, debe usar las propiedades de dependencia. INPC nunca mostrará una propiedad en la ventana de propiedades en tiempo de diseño.

John Peters
fuente
4

Parece que las Propiedades de dependencia deben usarse en controles que cree, como Botones. Para usar propiedades en XAML y usar todas las características de WPF, esas propiedades deben Propiedades de dependencia.

Sin embargo, su ViewModel está mejor usando INotifyPropertyChanged. El uso de INotifyPropertyChanged le dará la capacidad de tener lógica getter / setter si es necesario.

Recomiendo revisar la versión de Josh Smith de una clase base para un ViewModel que ya implementa INotifyPropertyChanged:

http://joshsmithonwpf.wordpress.com/2007/08/29/a-base-class-which-implements-inotifypropertychanged/

Creo que este es un excelente ejemplo de cómo hacer un ViewModel.

timothymcgrath
fuente
4

Creo que DependencyProperty e INotifyPropertyChanged se usan para dos cosas diferentes en Binding: la primera para permitir que una propiedad sea el objetivo de un enlace y recibir la entrada de otra propiedad (use {Binding ...} para establecer la propiedad), la última cuando desea que el valor de una propiedad se use como origen de un enlace (nombre en la Expresión de ruta de enlace). Entonces la elección es meramente técnica.

Domnik
fuente
2
Un INotifyPropertyChanged se puede utilizar en cualquier caso. Puedes vincular TwoWay a él. Se requiere una DependencyProperty por razones técnicas solo para algunas acciones realizadas en un objeto View (establecer algunas propiedades al instanciar un objeto View en XAML, por ejemplo). Nunca se requiere una DependencyProperty para un ViewModel.
oillio 01 de
3

Prefiero un enfoque más directo, sobre el cual escribí en un blog en Presentation Model Sin INotifyPropertyChanged . Usando una alternativa al enlace de datos, puede enlazar directamente a las propiedades CLR sin ningún código de contabilidad. Simplemente escribe código .NET simple en su Modelo de vista, y se actualiza cuando cambia su Modelo de datos.

Michael L Perry
fuente
Sin INotifyPropertyChanged, PropertyDescriptorse utilizan, lo que provoca pérdidas de memoria
Tilak
La biblioteca de controles de actualización que presento en esa publicación de blog usa referencias débiles, no descriptores de propiedades. No pierde memoria.
Michael L Perry
1
Michael, tu biblioteca genera mucho código. No veo beneficios Puedo lograr lo mismo generando un contenedor de modelo con llamadas de evento PropertyChanged generadas.
Der_Meister el
3

Solo hay una cosa por la que preferir un DependencyObject: el enlace funcionará mejor. Simplemente pruebe un ejemplo con una ListBoxy TextBox, complete la lista con los datos de la INotifyPropertyChangedpropiedad DependencyPropertyy edite el elemento actual desde TextBox...

ramos
fuente
1
Ejemplo de código, por favor
Hassan Tareq
1

Si desea exponer las propiedades a otros controles, debe usar las propiedades de dependencia ... Pero buena suerte porque tardan un tiempo en darse cuenta ...

JWP
fuente