¿Los convertidores de valor son más problemáticos de lo que valen?

20

Estoy trabajando en una aplicación WPF con vistas que requieren numerosas conversiones de valor. Inicialmente, mi filosofía (inspirada en parte por este animado debate sobre los Discípulos XAML ) era que debía hacer el modelo de vista estrictamente sobre el soporte de los requisitos de datos de la vista. Esto significaba que cualquier conversión de valor requerida para convertir datos en cosas como visibilidades, pinceles, tamaños, etc. se manejaría con convertidores de valor y convertidores de valores múltiples. Conceptualmente, esto parecía bastante elegante. El modelo de vista y la vista tendrían un propósito distinto y estarían muy bien desacopladas. Se trazaría una línea clara entre "datos" y "aspecto".

Bueno, después de dar a esta estrategia "el viejo intento de la universidad", tengo algunas dudas sobre si quiero seguir desarrollándome de esta manera. De hecho, estoy considerando descartar los convertidores de valor y poner la responsabilidad de (casi) toda la conversión de valor directamente en manos del modelo de vista.

La realidad del uso de convertidores de valor simplemente no parece estar a la altura del valor aparente de las preocupaciones separadas por separado. Mi mayor problema con los convertidores de valor es que son tediosos de usar. Debe crear una nueva clase, implementar IValueConvertero IMultiValueConverterconvertir el valor o valores objectal tipo correcto, probar DependencyProperty.Unset(al menos para convertidores de valores múltiples), escribir la lógica de conversión, registrar el convertidor en un diccionario de recursos [consulte la actualización a continuación ], y finalmente, conecte el convertidor usando XAML bastante detallado (que requiere el uso de cadenas mágicas tanto para el enlace como para el nombre del convertidor[ver actualización a continuación]). El proceso de depuración tampoco es fácil, ya que los mensajes de error a menudo son crípticos, especialmente en el modo de diseño de Visual Studio / Expression Blend.

Esto no quiere decir que la alternativa, hacer que el modelo de vista sea responsable de toda la conversión de valor, sea una mejora. Esto podría muy bien ser una cuestión de que la hierba sea más verde en el otro lado. Además de perder la elegante separación de las preocupaciones, debe escribir un montón de propiedades derivadas y asegurarse de llamar concienzudamente RaisePropertyChanged(() => DerivedProperty)al establecer las propiedades base, lo que podría ser un problema de mantenimiento desagradable.

La siguiente es una lista inicial que reuní de los pros y los contras de permitir que los modelos de vista manejen la lógica de conversión y eliminen los convertidores de valor:

  • Pros:
    • Menos enlaces totales ya que se eliminan los convertidores múltiples
    • Menos cadenas mágicas (rutas de enlace + nombres de recursos del convertidor )
    • No más registrar cada convertidor (además de mantener esta lista)
    • Menos trabajo para escribir cada convertidor (no se requieren interfaces de implementación ni conversión)
    • Puede inyectar dependencias fácilmente para ayudar con las conversiones (por ejemplo, tablas de colores)
    • El marcado XAML es menos detallado y más fácil de leer.
    • La reutilización del convertidor aún es posible (aunque se requiere algo de planificación)
    • No hay problemas misteriosos con DependencyProperty.Unset (un problema que noté con los convertidores de valores múltiples)

* Los tachados indican beneficios que desaparecen si usa extensiones de marcado (consulte la actualización a continuación)

  • Contras:
    • Un acoplamiento más fuerte entre el modelo de vista y la vista (por ejemplo, las propiedades deben tratar conceptos como visibilidad y pinceles)
    • Más propiedades totales para permitir la asignación directa para cada enlace a la vista
    • RaisePropertyChangeddebe llamarse para cada propiedad derivada (consulte la Actualización 2 a continuación)
    • Todavía debe confiar en los convertidores si la conversión se basa en una propiedad de un elemento de la interfaz de usuario

Entonces, como probablemente pueda notar, tengo un poco de acidez estomacal sobre este problema. Dudo mucho en seguir el camino de la refactorización solo para darme cuenta de que el proceso de codificación es tan ineficiente y tedioso si uso convertidores de valor o expongo numerosas propiedades de conversión de valor en mi modelo de vista.

¿Me estoy perdiendo algún pros / contras? Para aquellos que han intentado ambos medios de conversión de valor, ¿cuál encontró que funcionó mejor para usted y por qué? ¿Hay otras alternativas? (Los discípulos mencionaron algo sobre los proveedores de descriptores de tipo, pero no pude entender de qué estaban hablando. Cualquier idea sobre esto sería apreciada).


Actualizar

Hoy descubrí que es posible usar algo llamado "extensión de marcado" para eliminar la necesidad de registrar convertidores de valor. De hecho, no solo elimina la necesidad de registrarlos, sino que también proporciona inteligencia para seleccionar un convertidor cuando escribe Converter=. Aquí está el artículo que me ayudó a comenzar: http://www.wpftutorial.net/ValueConverters.html .

La capacidad de usar una extensión de marcado cambia el equilibrio de alguna manera en mi lista de pros y contras y en la discusión anterior (ver tachado).

Como resultado de esta revelación, estoy experimentando con un sistema híbrido donde utilizo convertidores BoolToVisibilityy lo que llamo MatchToVisibilityy el modelo de vista para todas las demás conversiones. MatchToVisibility es básicamente un convertidor que me permite verificar si el valor enlazado (generalmente una enumeración) coincide con uno o más valores especificados en XAML.

Ejemplo:

Visibility="{Binding Status, Converter={vc:MatchToVisibility
            IfTrue=Visible, IfFalse=Hidden, Value1=Finished, Value2=Canceled}}"

Básicamente, lo que hace es verificar si el estado es Finalizado o Cancelado. Si es así, la visibilidad se establece en "Visible". De lo contrario, se establece en "Oculto". Esto resultó ser un escenario muy común, y tener este convertidor me ahorró alrededor de 15 propiedades en mi modelo de vista (más las declaraciones RaisePropertyChanged asociadas). Tenga en cuenta que cuando escribe Converter={vc:, "MatchToVisibility" aparece en un menú inteligente. Esto reduce notablemente la posibilidad de errores y hace que el uso de convertidores de valor sea menos tedioso (no tiene que recordar ni buscar el nombre del convertidor de valor que desea).

En caso de que tenga curiosidad, pegaré el código a continuación. Una característica importante de esta implementación de MatchToVisibilityes que se comprueba si el valor límite es una enum, y si lo es, se comprueba para asegurarse Value1, Value2, etc., también son enumeraciones del mismo tipo. Esto proporciona una verificación en tiempo de diseño y tiempo de ejecución de si alguno de los valores de enumeración está mal escrito. Para mejorar esto a una verificación en tiempo de compilación, puede usar lo siguiente (escribí esto a mano, así que perdóneme si cometí algún error):

Visibility="{Binding Status, Converter={vc:MatchToVisibility
            IfTrue={x:Type {win:Visibility.Visible}},
            IfFalse={x:Type {win:Visibility.Hidden}},
            Value1={x:Type {enum:Status.Finished}},
            Value2={x:Type {enum:Status.Canceled}}"

Si bien esto es más seguro, es demasiado detallado para que valga la pena para mí. También podría usar una propiedad en el modelo de vista si voy a hacer esto. De todos modos, descubro que la verificación en tiempo de diseño es perfectamente adecuada para los escenarios que he probado hasta ahora.

Aquí está el código para MatchToVisibility

[ValueConversion(typeof(object), typeof(Visibility))]
public class MatchToVisibility : BaseValueConverter
{
    [ConstructorArgument("ifTrue")]
    public object IfTrue { get; set; }

    [ConstructorArgument("ifFalse")]
    public object IfFalse { get; set; }

    [ConstructorArgument("value1")]
    public object Value1 { get; set; }

    [ConstructorArgument("value2")]
    public object Value2 { get; set; }

    [ConstructorArgument("value3")]
    public object Value3 { get; set; }

    [ConstructorArgument("value4")]
    public object Value4 { get; set; }

    [ConstructorArgument("value5")]
    public object Value5 { get; set; }

    public MatchToVisibility() { }

    public MatchToVisibility(
        object ifTrue, object ifFalse,
        object value1, object value2 = null, object value3 = null,
        object value4 = null, object value5 = null)
    {
        IfTrue = ifTrue;
        IfFalse = ifFalse;
        Value1 = value1;
        Value2 = value2;
        Value3 = value3;
        Value4 = value4;
        Value5 = value5;
    }

    public override object Convert(
        object value, Type targetType, object parameter, CultureInfo culture)
    {
        var ifTrue = IfTrue.ToString().ToEnum<Visibility>();
        var ifFalse = IfFalse.ToString().ToEnum<Visibility>();
        var values = new[] { Value1, Value2, Value3, Value4, Value5 };
        var valueStrings = values.Cast<string>();
        bool isMatch;
        if (Enum.IsDefined(value.GetType(), value))
        {
            var valueEnums = valueStrings.Select(vs => vs == null ? null : Enum.Parse(value.GetType(), vs));
            isMatch = valueEnums.ToList().Contains(value);
        }
        else
            isMatch = valueStrings.Contains(value.ToString());
        return isMatch ? ifTrue : ifFalse;
    }
}

Aquí está el código para BaseValueConverter

// this is how the markup extension capability gets wired up
public abstract class BaseValueConverter : MarkupExtension, IValueConverter
{
    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        return this;
    }

    public abstract object Convert(
        object value, Type targetType, object parameter, CultureInfo culture);

    public virtual object ConvertBack(
        object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

Aquí está el método de extensión ToEnum

public static TEnum ToEnum<TEnum>(this string text)
{
    return (TEnum)Enum.Parse(typeof(TEnum), text);
}

Actualización 2

Desde que publiqué esta pregunta, me encontré con un proyecto de código abierto que usa "tejido de IL" para inyectar código NotifyPropertyChanged para propiedades y propiedades dependientes. Esto hace que implementar la visión de Josh Smith del modelo de vista como un "convertidor de valor con esteroides" sea una brisa absoluta. Simplemente puede usar "Propiedades de implementación automática", y el tejedor hará el resto.

Ejemplo:

Si ingreso este código:

public string GivenName { get; set; }
public string FamilyName { get; set; }

public string FullName
{
    get
    {
        return string.Format("{0} {1}", GivenName, FamilyName);
    }
}

... esto es lo que se compila:

string givenNames;
public string GivenNames
{
    get { return givenName; }
    set
    {
        if (value != givenName)
        {
            givenNames = value;
            OnPropertyChanged("GivenName");
            OnPropertyChanged("FullName");
        }
    }
}

string familyName;
public string FamilyName
{
    get { return familyName; }
    set 
    {
        if (value != familyName)
        {
            familyName = value;
            OnPropertyChanged("FamilyName");
            OnPropertyChanged("FullName");
        }
    }
}

public string FullName
{
    get
    {
        return string.Format("{0} {1}", GivenName, FamilyName);
    }
}

Eso es un gran ahorro en la cantidad de código que tiene que escribir, leer, desplazarse, etc. Más importante, sin embargo, le ahorra tener que averiguar cuáles son sus dependencias. Puede agregar nuevas "propiedades obtiene" comoFullName sin tener que subir minuciosamente la cadena de dependencias para agregar RaisePropertyChanged()llamadas.

¿Cómo se llama este proyecto de código abierto? La versión original se llama "NotifyPropertyWeaver", pero el propietario (Simon Potter) ha creado una plataforma llamada "Fody" para alojar una serie completa de tejedores IL. El equivalente de NotifyPropertyWeaver bajo esta nueva plataforma se llama PropertyChanged.Fody.

Si prefiere usar NotifyPropertyWeaver (que es un poco más simple de instalar, pero no necesariamente se actualizará en el futuro más allá de las correcciones de errores), aquí está el sitio del proyecto: http://code.google.com/p/ notifiquepropiedadweaver /

De cualquier manera, estas soluciones de tejido IL cambian completamente el cálculo en el debate entre el modelo de vista sobre esteroides y los convertidores de valor.

devuxer
fuente
Solo una nota: BooleanToVisibilitytoma un valor relacionado con la visibilidad (verdadero / falso) y lo traduce a otro. Esto parece un uso ideal de a ValueConverter. Por otro lado, MatchToVisibilityestá codificando la lógica de negocios en View(qué tipos de elementos deberían estar visibles). En mi opinión, esta lógica debería ser empujada hacia abajo ViewModel, o incluso más allá de lo que yo llamo EditModel. Lo que el usuario puede ver debería ser algo bajo prueba.
Scott Whitlock
@ Scott, ese es un buen punto. La aplicación en la que estoy trabajando en este momento no es realmente una aplicación "comercial", donde hay diferentes niveles de permisos para los usuarios, por lo que no estaba pensando en ese sentido. MatchToVisibilityparecía ser una forma conveniente de habilitar algunos interruptores de modo simples (tengo una vista en particular con un montón de partes que se pueden encender y apagar. En la mayoría de los casos, las secciones de la vista incluso están etiquetadas (con x:Name) para que coincidan con el modo corresponden a). Realmente no se me ocurrió que esto es "lógica de negocios", pero pensaré en su comentario.
devuxer
Ejemplo: supongamos que tenía un sistema estéreo que podría estar en modo radio, CD o MP3. Suponga que hay imágenes correspondientes a cada modo en diferentes partes de la interfaz de usuario. Puede (1) dejar que la vista decida qué gráficos corresponden a qué modo y activarlos / desactivarlos en consecuencia, (2) exponer propiedades en el modelo de vista para cada valor de modo (por ejemplo, IsModeRadio, IsModeCD), o (3) exponer propiedades en el modelo de vista para cada elemento / grupo gráfico (por ejemplo, IsRadioLightOn, IsCDButtonGroupOn). (1) parecía un ajuste natural para mi punto de vista, porque ya tiene conciencia de modo. ¿Qué opinas en este caso?
devuxer
¡Esta es la pregunta más larga que he visto en todo el SE! :]
trejder

Respuestas:

10

Lo he usado ValueConvertersen algunos casos y puse la lógica ViewModelen otros. Mi sensación es que a se ValueConverterconvierte en parte de la Viewcapa, por lo que si la lógica es realmente parte de la capa, Viewpóngala allí, de lo contrario póngala en elViewModel .

Personalmente, no veo ningún problema al ViewModeltratar con Viewconceptos específicos como Brushes porque en mis aplicaciones ViewModelsolo existe una superficie comprobable y enlazable para el View. Sin embargo, algunas personas ponen mucha lógica de negocios en el ViewModel(yo no) y en ese caso elViewModel es más como una parte de su capa de negocios, por lo que en ese caso no querría cosas específicas de WPF allí.

Prefiero una separación diferente:

  • View- Cosas de WPF, a veces no comprobables (como XAML y código subyacente) pero también ValueConverters
  • ViewModel - clase comprobable y enlazable que también es específica de WPF
  • EditModel - parte de la capa empresarial que representa mi modelo durante la manipulación
  • EntityModel - parte de la capa empresarial que representa mi modelo como persistente
  • Repository- responsable de la persistencia de la EntityModela la base de datos

Entonces, la forma en que lo hago, tengo poco uso para ValueConverter s

La forma en que me alejé de algunos de tus "Con" es hacer que mis ViewModel"genéricos" sean muy genéricos. Por ejemplo, uno ViewModelque tengo, llamado ChangeValueViewModelimplementa una propiedad Label y una propiedad Value. En el Viewhay un Labelenlace a la propiedad Label y un TextBoxenlace a la propiedad Value.

Entonces tengo uno ChangeValueViewque está DataTemplatedesconectado del ChangeValueViewModeltipo. Cada vez que WPF ve que ViewModelaplica eso View. El constructor de my ChangeValueViewModeltoma la lógica de interacción que necesita para actualizar su estado desde EditModel(generalmente solo pasando una Func<string>) y la acción que debe tomar cuando el usuario edita el Valor (solo una Actionque ejecuta alguna lógica en elEditModel ).

El padre ViewModel(para la pantalla) toma un EditModelen su constructor y solo crea una instancia de los elementos elementales apropiados ViewModelcomo ChangeValueViewModel. Dado que el padre ViewModelestá inyectando la acción a realizar cuando el usuario realiza algún cambio, puede interceptar todas estas acciones y tomar otras acciones. Por lo tanto, la acción de edición inyectada para un ChangeValueViewModelpodría verse así:

(string newValue) =>
{
    editModel.SomeField = newValue;
    foreach(var childViewModel in this.childViewModels)
    {
        childViewModel.RefreshStateFromEditModel();
    }
}

Obviamente, el foreachbucle se puede refactorizar en otro lugar, pero lo que esto hace es tomar la acción, aplicarlo al modelo y luego (suponiendo que el modelo haya actualizado su estado de alguna manera desconocida), les dice a todos los niños ViewModelque vayan y obtengan su estado. El modelo de nuevo. Si el estado ha cambiado, son responsables de ejecutar suPropertyChanged eventos, según sea necesario.

Eso maneja la interacción entre, digamos, un cuadro de lista y un panel de detalles bastante bien. Cuando el usuario selecciona una nueva opción, la actualiza EditModelcon la opción y EditModelcambia los valores de las propiedades expuestas para el panel de detalles. Los ViewModelniños que son responsables de mostrar la información del panel de detalles reciben automáticamente una notificación de que necesitan verificar nuevos valores y, si han cambiado, activan sus PropertyChangedeventos.

Scott Whitlock
fuente
/cabecear. Eso es bastante similar a cómo se ve el mío.
Ian
+1. Gracias por tu respuesta, Scott, tengo casi las mismas capas que tú, y tampoco pongo lógica de negocios en el modelo de vista. (Para el registro, estoy usando EntityFramework Code First, y tengo una capa de servicio que se traduce entre modelos de vista y modelos de entidad, y viceversa). Entonces, dado esto, supongo que estás diciendo que no hay mucho costo para poner toda / la mayor parte de la lógica de conversión en la capa del modelo de vista.
devuxer
@DanM - Sí, estoy de acuerdo. Preferiría la conversión en la ViewModelcapa. No todos están de acuerdo conmigo, pero depende de cómo funcione su arquitectura.
Scott Whitlock
2
Iba a decir +1 después de leer el primer párrafo, pero luego leí el segundo y estoy totalmente en desacuerdo con poner código específico de vista en ViewModels. La única excepción es si ViewModel se crea específicamente para ir detrás de una Vista genérica (como CalendarViewModelpara un CalendarViewUserControl o un DialogViewModelpara a DialogView). Sin embargo, esa es solo mi opinión :)
Rachel
@Rachel: bueno, si hubieras seguido leyendo más allá de mi segundo párrafo, verías que eso es exactamente lo que estaba haciendo. :) No hay lógica de negocios en mi ViewModels.
Scott Whitlock
8

Si la conversión es algo relacionado con la vista, como decidir la visibilidad de un objeto, determinar qué imagen mostrar o averiguar qué color de pincel usar, siempre pongo mis convertidores en la vista.

Si está relacionado con el negocio, como determinar si un campo debe enmascararse, o si un usuario tiene permiso para realizar una acción, la conversión se realiza en mi ViewModel.

Desde sus ejemplos, creo que se está perdiendo una gran pieza de WPF: DataTriggers. Parece que está usando convertidores para determinar valores condicionales, pero los convertidores realmente deberían ser para convertir un tipo de datos en otro.

En tu ejemplo anterior

Ejemplo: supongamos que tenía un sistema estéreo que podría estar en modo radio, CD o MP3. Suponga que hay imágenes correspondientes a cada modo en diferentes partes de la interfaz de usuario. Puede (1) dejar que la vista decida qué gráficos corresponden a qué modo y activarlos / desactivarlos en consecuencia, (2) exponer propiedades en el modelo de vista para cada valor de modo (por ejemplo, IsModeRadio, IsModeCD), o (3) exponer propiedades en el modelo de vista para cada elemento / grupo gráfico (por ejemplo, IsRadioLightOn, IsCDButtonGroupOn). (1) parecía un ajuste natural para mi punto de vista, porque ya tiene conciencia de modo. ¿Qué opinas en este caso?

Usaría a DataTriggerpara determinar qué imagen mostrar, no a Converter. Un convertidor es para convertir un tipo de datos a otro, mientras que un activador se utiliza para determinar algunas propiedades en función de un valor.

<Style x:Key="RadioImageStyle">
    <Setter Property="Source" Value="{StaticResource RadioImage}" />
    <Style.Triggers>
        <DataTrigger Binding="{Binding Mode}" Value="CD">
            <Setter Property="Source" Value="{StaticResource CDImage}" />
        </DataTrigger>
        <DataTrigger Binding="{Binding Mode}" Value="MP3">
            <Setter Property="Source" Value="{StaticResource MP3Image}" />
        </DataTrigger>
    </Style.Triggers>
</Style>

La única vez que consideraría usar un convertidor para esto es si el valor enlazado realmente contiene los datos de la imagen, y necesito convertirlo en un tipo de datos que la IU pueda entender. Por ejemplo, si la fuente de datos contiene una propiedad llamada ImageFilePath, entonces consideraría usar un convertidor para convertir la cadena que contiene la ubicación del archivo de imagen en una BitmapImageque podría usarse como la fuente de mi imagen

<Style x:Key="RadioImageStyle">
    <Setter Property="Source" Value="{Binding ImageFilePath, 
            Converter={StaticResource StringPathToBitmapConverter}}" />
</Style>

El resultado final es que tengo un espacio de nombres de biblioteca lleno de convertidores genéricos que convierten un tipo de datos a otro, y rara vez tengo que codificar un nuevo convertidor. Hay ocasiones en las que querré convertidores para conversiones específicas, pero son tan poco frecuentes que no me importa escribirlas.

Rachel
fuente
+1. Subes algunos buenos puntos. He usado disparadores antes, pero en mi caso, no estoy cambiando las fuentes de imágenes (que son una propiedad), estoy cambiando Gridelementos completos . También estoy tratando de hacer cosas como establecer pinceles para primer plano / fondo / trazo en función de los datos en mi modelo de vista y una paleta de colores específica definida en el archivo de configuración. No estoy seguro de que este sea un gran ajuste para un disparador o un convertidor. El único problema que tengo hasta ahora al poner la mayoría de la lógica de vista en el modelo de vista es conectar todas las RaisePropertyChanged()llamadas.
devuxer
@DanM Realmente haría todas esas cosas en un DataTrigger, incluso cambiando los elementos de Grid. Por lo general, coloco un lugar ContentControldonde debería estar mi contenido dinámico y lo cambio ContentTemplateen un disparador. Tengo un ejemplo en el siguiente enlace si está interesado (desplácese hacia abajo a la sección con el encabezado de Using a DataTrigger) rachel53461.wordpress.com/2011/05/28/…
Rachel
He usado plantillas de datos y controles de contenido antes, pero nunca he necesitado disparadores porque siempre he tenido un modelo de vista único para cada vista. De todos modos, su técnica tiene mucho sentido y es bastante elegante, pero también es muy detallada. Con MatchToVisibility, podría acortarse a esto: <TextBlock Text="I'm a Person" Visibility={Binding ConsumerType, Converter={vc:MatchToVisibility IfTrue=Visible, IfFalse=Hidden, Value1=Person}}"y<TextBlock Text="I'm a Business" Visibility={Binding ConsumerType, Converter={vc:MatchToVisibility IfTrue=Visible, IfFalse=Hidden, Value1=Business}}"
devuxer
1

Depende de lo que esté probando , en todo caso.

Sin pruebas: mezcle el código de vista con ViewModel a voluntad (siempre puede refactorizar más adelante).
Pruebas en ViewModel y / o versiones inferiores: use convertidores.
Pruebas en capas de modelo y / o inferiores: entremezcle el código de vista con ViewModel a voluntad

ViewModel abstrae el Modelo para la Vista . Personalmente, usaría ViewModel para Pinceles, etc. y me saltaría los convertidores. Pruebe en la (s) capa (s) donde los datos están en su forma " más pura " (es decir, capas de modelo ).

Jake Berger
fuente
2
Puntos interesantes sobre las pruebas, pero supongo que no veo cómo tener lógica de convertidor en el modelo de vista perjudica la capacidad de prueba del modelo de vista. Ciertamente no estoy sugiriendo poner controles de UI reales en el modelo de vista. Sólo específicas para la vista propiedades como Visibility, SolidColorBrushy Thickness.
devuxer
@DanM: Si está utilizando un enfoque de Ver primero , entonces no hay problema . Sin embargo, algunos usan un enfoque de ViewModel-first donde ViewModel hace referencia a una Vista, puede ser problemático .
Jake Berger
Hola Jay, definitivamente un enfoque de primera vista. La vista no sabe nada sobre el modelo de vista, excepto los nombres de las propiedades a las que debe vincularse. Gracias por seguirnos. +1.
devuxer
0

Esto probablemente no resolverá todos los problemas que mencionó, pero hay dos puntos a considerar:

Primero, debe colocar el código del convertidor en algún lugar de su primera estrategia. ¿Considera esa parte de la vista o el modelo de vista? Si es parte de la vista, ¿por qué no colocar las propiedades específicas de la vista en la vista en lugar del modelo de vista?

En segundo lugar, parece que su diseño sin convertidor intenta modificar las propiedades reales de los objetos que ya existen. Parece que ya implementan INotifyPropertyChanged, entonces, ¿por qué no usar crear un objeto contenedor específico de vista para enlazar? Aquí hay un ejemplo simple:

public class RealData
{
    private bool mIsInteresting;
    public bool IsInteresting
    {
        get { return mIsInteresting; }
        set 
        {
            if (mIsInteresting != null) 
            {
                mIsInteresting = value;
                RaisePropertyChanged("IsInteresting");
            }
        }
    }
}

public class RealDataView
{
    private RealData mRealData;

    public RealDataView(RealData data)
    {
        mRealData = data;
        mRealData.PropertyChanged += OnRealDataPropertyChanged;
    }

    public Visibility IsVisiblyInteresting
    {
       get { return mRealData.IsInteresting ? Visibility.Visible : Visibility.Hidden; }
       set { mRealData.IsInteresting = (value == Visibility.Visible); }
    }

    private void OnRealDataPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if (e.PropertyName == "IsInteresting") 
        {
            RaisePropertyChanged(this, "IsVisiblyInteresting");
        }
    }
}
John Fisher
fuente
No quise decir que estoy cambiando las propiedades de mi modelo de entidad directamente en la vista o modelo de vista. El modelo de vista es definitivamente una capa diferente que la capa de mi modelo de entidad. De hecho, el trabajo que he realizado hasta ahora en vistas de solo lectura. Esto no quiere decir que mi aplicación no implique ninguna edición, pero no veo que se realicen conversiones en los controles utilizados para la edición (así que suponga que todos los enlaces son unidireccionales, excepto las selecciones en las listas). Buen punto sobre "vistas de datos" sin embargo. Ese fue un concepto planteado en la publicación de los discípulos de XAML a la que me referí al principio de mi pregunta.
devuxer
0

A veces es bueno usar un convertidor de valor para aprovechar la virtualización.

Un ejemplo de esto es qué en un proyecto en el que tuvimos que mostrar datos enmascarados para cientos de miles de celdas en una cuadrícula. Cuando decodificamos las máscaras de bits en el modelo de vista para cada celda, el programa tardó demasiado en cargarse.

Pero cuando creamos un convertidor de valor que decodificó una sola celda, el programa se cargó en una fracción del tiempo y fue igual de receptivo porque el convertidor solo se llama cuando el usuario está mirando una celda en particular (y solo necesitaría ser llamado un máximo de treinta veces cada vez que el usuario cambia su vista en la cuadrícula).

No sé cómo MVVM se quejó de esa solución, pero redujo el tiempo de carga en un 95%.

Kleineg
fuente