Cuando quiero enlazar un control a una propiedad de mi objeto, tengo que proporcionar el nombre de la propiedad como una cadena. Esto no es muy bueno porque:

  1. Si la propiedad es eliminado o
    cambia el nombre, no tengo un compilador
    advertencia.
  2. Si cambiar el nombre de la propiedad
    con una herramienta de refactorización, es
    probablemente, el enlace de datos no se
    actualizado.
  3. No tengo un error hasta
    el tiempo de ejecución si el tipo de la propiedad
    es incorrecto, por ejemplo, de unión de un número entero
    un selector de fecha.

Hay un diseño de patrón que se obtiene de la ronda este, pero todavía tiene la facilidad de uso de enlace de datos?

(Este es un problema en WinForm Asp.net y WPF y lo más probable es que muchos de los otros sistemas)

Ahora he encontrado «soluciones para nameof operador() en C#: typesafe databinding» que también tiene un buen punto de partida para una solución.

Si usted está dispuesto a usar un post procesador después de compilar el código, notifypropertyweaver es bien vale la pena mirar.


Alguien sabe de una buena solución para WPF cuando los enlaces se realizan en XML en lugar de C#?

  • Más referencias: jagregory.com/writings/introduction-to-static-reflection lostechies.com/blogs/gabrielschenker/archive/2009/02/03/… weblogs.asp.net/cazzu/archive/2006/07/06/…
  • Citando el vinculado pregunta: Este problema está resuelto en tiempo de compilación! El nameof operador fue implementado en C# 6.0 .NET 4.6 y VS2015 en julio de 2015. Las siguientes respuestas son válidas para C#, < 6.0. – Mike (stackoverflow.com/users/772086/mike)
  • eso no resuelve más que la esperanza, como no es utilizable desde XAML y no da la seguridad de tipos. No obstante, se permitirá la refactorización cuando la unión se realiza a partir de «código».
  • Justo lo suficiente, el problema no se soluciona hasta que tengamos tiempo de compilación tipo de seguridad/de la capacidad de uso de marcado como XAML. Sin embargo, mi idea principal era, que la solución en la aceptación de la respuesta (BindingHelper) no debe ser utilizado en C# 6.0 y posteriores, donde la misma puede lograrse mediante el nameof operador. Ahora la respuesta refleja esto, así que estoy feliz 🙂
  • Vea el enlace de cómo detectar en XAML rotos los enlaces ya en tiempo de compilación: stackoverflow.com/questions/43208011/…
InformationsquelleAutor Ian Ringrose | 2009-08-25

7 Comentarios

  1. 51

    Gracias a Oliver para hacer que me inició, ahora tengo una solución que tanto apoya la refactorización y es el tipo de seguro. También me deja aplicar INotifyPropertyChanged así se arregla con las propiedades de ser rebautizado.

    Su uso parece:

    checkBoxCanEdit.Bind(c => c.Checked, person, p => p.UserCanEdit);
    textBoxName.BindEnabled(person, p => p.UserCanEdit);
    checkBoxEmployed.BindEnabled(person, p => p.UserCanEdit);
    trackBarAge.BindEnabled(person, p => p.UserCanEdit);
    
    textBoxName.Bind(c => c.Text, person, d => d.Name);
    checkBoxEmployed.Bind(c => c.Checked, person, d => d.Employed);
    trackBarAge.Bind(c => c.Value, person, d => d.Age);
    
    labelName.BindLabelText(person, p => p.Name);
    labelEmployed.BindLabelText(person, p => p.Employed);
    labelAge.BindLabelText(person, p => p.Age);

    La clase de persona se muestra cómo se implementa INotifyPropertyChanged en un tipo de manera segura (o ver esta respuesta para otro lugar agradable manera de implementar INotifyPropertyChanged, ActiveSharp – Automático INotifyPropertyChanged también se ve bien ):

    public class Person : INotifyPropertyChanged
    {
       private bool _employed;
       public bool Employed
       {
          get { return _employed; }
          set
          {
             _employed = value;
             OnPropertyChanged(() => c.Employed);
          }
       }
    
       //etc
    
       private void OnPropertyChanged(Expression<Func<object>> property)
       {
          if (PropertyChanged != null)
          {
             PropertyChanged(this, 
                 new PropertyChangedEventArgs(BindingHelper.Name(property)));
          }
       }
    
       public event PropertyChangedEventHandler PropertyChanged;
    }

    La WinForms unión de ayudante de clase de la carne en lo que hace todo el trabajo:

    namespace TypeSafeBinding
    {
    public static class BindingHelper
    {
    private static string GetMemberName(Expression expression)
    {
    //The nameof operator was implemented in C# 6.0 with .NET 4.6
    //and VS2015 in July 2015. 
    //The following is still valid for C# < 6.0
    switch (expression.NodeType)
    {
    case ExpressionType.MemberAccess:
    var memberExpression = (MemberExpression) expression;
    var supername = GetMemberName(memberExpression.Expression);
    if (String.IsNullOrEmpty(supername)) return memberExpression.Member.Name;
    return String.Concat(supername, '.', memberExpression.Member.Name);
    case ExpressionType.Call:
    var callExpression = (MethodCallExpression) expression;
    return callExpression.Method.Name;
    case ExpressionType.Convert:
    var unaryExpression = (UnaryExpression) expression;
    return GetMemberName(unaryExpression.Operand);
    case ExpressionType.Parameter:
    case ExpressionType.Constant: //Change
    return String.Empty;
    default:
    throw new ArgumentException("The expression is not a member access or method call expression");
    }
    }
    public static string Name<T, T2>(Expression<Func<T, T2>> expression)
    {
    return GetMemberName(expression.Body);
    }
    //NEW
    public static string Name<T>(Expression<Func<T>> expression)
    {
    return GetMemberName(expression.Body);
    }
    public static void Bind<TC, TD, TP>(this TC control, Expression<Func<TC, TP>> controlProperty, TD dataSource, Expression<Func<TD, TP>> dataMember) where TC : Control
    {
    control.DataBindings.Add(Name(controlProperty), dataSource, Name(dataMember));
    }
    public static void BindLabelText<T>(this Label control, T dataObject, Expression<Func<T, object>> dataMember)
    {
    //as this is way one any type of property is ok
    control.DataBindings.Add("Text", dataObject, Name(dataMember));
    }
    public static void BindEnabled<T>(this Control control, T dataObject, Expression<Func<T, bool>> dataMember)
    {       
    control.Bind(c => c.Enabled, dataObject, dataMember);
    }
    }
    }

    Esto hace que el uso de un montón de cosas nuevas en C# 3.5 y muestra lo que es posible. Ahora, si tan sólo tuviéramos higiene macros lisp programador puede dejar de llamarnos ciudadanos de segunda clase)

    • ¿Esto requiere que el OnPropertyChanged el método implementado para cada tipo? Si es así, es algo agradable, pero no es ideal, y muchas veces la OnPropertyChanged método se implementa en una clase base y el llamado de todas las clases derivadas.
    • Davy, no hay ninguna razón por qué OnPropertyChanged método (y evento) no sólo podría ser trasladado a una clase base y protegidos. (Eso es lo que yo esperaría que hacer en la vida real)
    • Pero a partir de su ejemplo, parece que se basa en el parámetro de Expresión de tipo<Func<Persona, objeto>>, no el método deben ser aplicadas para cada tipo para tomar un parámetro de Expresión de tipo<Func<Foo, objeto>>, la Expresión<Func<Bar, objeto>>, etc?
    • Ahora he cambiado OnPropertyChanged a OnPropertyChanged(Expresión<Func<objeto>> propiedad) que le permiten moverse en la clase base.
    • véase también stackoverflow.com/questions/527602/… por otra forma de implementar OnPropertyChanged – (yo estaba tratando de hacer de Enlace de datos tipo de seguro, el OnPropertyChanged era sólo un lado de la línea utilizando el mismo método, que me gusta bastante la forma en la otra respuesta, utiliza un método de extensión en PropertyChangedEventHandler)
    • Lo que el objeto de Control ha de propiedades tales como DataBindings? He comprobado MSDN, no hay tal propiedad.
    • consulte msdn.microsoft.com/en-us/library/…
    • 🙂 Es WinForms, supuse que era de WPF. Gracias por la aclaración.
    • Para cuadros combinados uso: [combobox].ValueMember = BindingHelper.Name(Function(x as [myClass]) x.[Property]) asumiendo que su uso VB.Net y enlace a un IList. La clase anterior no parece manejar IList de unión muy bien, probablemente porque DataBindings.Add no funciona bien para la unión de una interfaz IList.

  2. 27

    La nameof operador fue implementado en C# 6.0 .NET 4.6 y VS2015 en julio de 2015. El siguiente es válido para C# < 6.0

    Para evitar que las cadenas que contienen los nombres de propiedad, he escrito una clase simple de usar árboles de expresión para devolver el nombre del miembro:

    using System;
    using System.Linq.Expressions;
    using System.Reflection;
    public static class Member
    {
    private static string GetMemberName(Expression expression)
    {
    switch (expression.NodeType)
    {
    case ExpressionType.MemberAccess:
    var memberExpression = (MemberExpression) expression;
    var supername = GetMemberName(memberExpression.Expression);
    if (String.IsNullOrEmpty(supername))
    return memberExpression.Member.Name;
    return String.Concat(supername, '.', memberExpression.Member.Name);
    case ExpressionType.Call:
    var callExpression = (MethodCallExpression) expression;
    return callExpression.Method.Name;
    case ExpressionType.Convert:
    var unaryExpression = (UnaryExpression) expression;
    return GetMemberName(unaryExpression.Operand);
    case ExpressionType.Parameter:
    return String.Empty;
    default:
    throw new ArgumentException("The expression is not a member access or method call expression");
    }
    }
    public static string Name<T>(Expression<Func<T, object>> expression)
    {
    return GetMemberName(expression.Body);
    }
    public static string Name<T>(Expression<Action<T>> expression)
    {
    return GetMemberName(expression.Body);
    }
    }

    Puede utilizar esta clase de la siguiente manera. Aunque sólo se puede usar en el código (por lo que no en XAML), es muy útil (al menos para mí), pero el código no es todavía typesafe. Se podría extender el Nombre de método con un segundo tipo de argumento que define el valor de retorno de la función, lo que podría limitar el tipo de la propiedad.

    var name = Member.Name<MyClass>(x => x.MyProperty); //name == "MyProperty"

    Hasta ahora no he encontrado nada que se soluciona el enlace de datos typesafety problema.

    Saludos

    • Gracias por el gran punto de partida, sólo he publicado una respuesta que extiende su trabajo a dar typesafety.
  3. 24

    El Framework 4.5 nos ofrece la CallerMemberNameAttribute, que hace pasar el nombre de la propiedad como una cadena innecesaria:

    private string m_myProperty;
    public string MyProperty
    {
    get { return m_myProperty; }
    set
    {
    m_myProperty = value;
    OnPropertyChanged();
    }
    }
    private void OnPropertyChanged([CallerMemberName] string propertyName = "none passed")
    {
    //... do stuff here ...
    }

    Si estás trabajando en Framework 4.0 con KB2468871 instalado, puede instalar el Microsoft BCL Paquete de Compatibilidad de a través de nuget, que también ofrece este atributo.

  4. 5

    Este blog el artículo plantea algunos interrogantes sobre el rendimiento de este enfoque. Puede mejorar esas deficiencias mediante la conversión de la expresión a una cadena como parte de algún tipo de inicialización estática.

    La mecánica real podría ser un poco feo, pero no por ello deja de ser de tipo seguro, y aproximadamente el mismo rendimiento para el raw INotifyPropertyChanged.

    Algo como esto:

    public class DummyViewModel : ViewModelBase
    {
    private class DummyViewModelPropertyInfo
    {
    internal readonly string Dummy;
    internal DummyViewModelPropertyInfo(DummyViewModel model)
    {
    Dummy = BindingHelper.Name(() => model.Dummy);
    }
    }
    private static DummyViewModelPropertyInfo _propertyInfo;
    private DummyViewModelPropertyInfo PropertyInfo
    {
    get { return _propertyInfo ?? (_propertyInfo = new DummyViewModelPropertyInfo(this)); }
    }
    private string _dummyProperty;
    public string Dummy
    {
    get
    {
    return this._dummyProperty;
    }
    set
    {
    this._dummyProperty = value;
    OnPropertyChanged(PropertyInfo.Dummy);
    }
    }
    }
    • buen punto, sin embargo, en la mayoría de los programas no es lickly a ser un problema en la vida real, así que trate de la forma más sencilla de primera.
  5. 3

    Una manera de obtener retroalimentación si los enlaces están rotos, es crear una plantilla de datos y declarar su Tipo de datos a ser el tipo de la ViewModel a la que se une, por ejemplo, si usted tiene un PersonView y un PersonViewModel, debe hacer lo siguiente:

    1. Declarar un DataTemplate con DataType = PersonViewModel y una clave (por ejemplo, PersonTemplate)

    2. Cortar todos los PersonView xaml y pegarlo en la plantilla de datos (que, idealmente, sólo puede ser en la parte superior de la PersonView.

    3a. Crear un ContentControl y establecer el ContentTemplate = PersonTemplate y enlazar su Contenido a la PersonViewModel.

    3b. Otra opción es no dar una clave a la plantilla de datos y no se establece la ContentTemplate de la ContentControl. En este caso WPF puede determinar qué plantilla de datos a usar, ya que se sabe qué tipo de objeto que son vinculantes para. Va a buscar el árbol y encontrar su DataTemplate y ya que coincide con el tipo de la unión, se aplicará automáticamente como el ContentTemplate.

    Usted termina con esencialmente el mismo punto de vista como antes, pero ya que usted asigna la plantilla de datos a un subyacente Tipo de datos, las herramientas de Resharper puede dar retroalimentación (a través de identificadores de Color – Resharper-Opciones-Configuración de Color de los Identificadores), como si los enlaces están rotos o no.

    Todavía no obtener las advertencias del compilador, pero visualmente puede comprobar enlaces rotos, que es mejor que tener que comprobar de ida y vuelta entre la vista y el viewmodel.

    Otra ventaja de esta información adicional se le dé, es decir, que también puede ser utilizado en el cambio de nombre de refactorings. Que yo recuerde Resharper es capaz de cambiar automáticamente el nombre de los enlaces escribió DataTemplates cuando el subyacente ViewModel de la propiedad se cambia el nombre y viceversa.

  6. 3

    1.Si la propiedad es eliminado o cambiado de nombre, no tengo una advertencia del compilador.

    2.Si cambiar el nombre de la propiedad con una herramienta de refactorización, es probable que el enlace de datos no se actualiza.

    3.No me sale un error que hasta el tiempo de ejecución si el tipo de la propiedad está mal, por ejemplo, de unión de un entero a un selector de fecha.

    Sí, Ian, que son exactamente los problemas con el nombre de cadena impulsada por el enlace de datos. Se le preguntó por un diseño de patrón. He diseñado el Seguro de tipos de Modelo de Vista (TVM) patrón que es una concreción del Modelo de Vista parte de la Model-View-ViewModel (MVVM) el patrón. Se basa en un tipo de seguro de unión, similares a los de su propia respuesta. He publicado una solución para WPF:

    http://www.codeproject.com/Articles/450688/Enhanced-MVVM-Design-w-Type-Safe-View-Models-TVM

    • Bueno, pero parece un montón de trabajo y un perturbador de turno de la unión en XAML de la unión en el Código Detrás cuando todos MSFT que había que hacer era realmente compilar los enlaces de XAML. Se compiló en BAML en cualquier caso, de lo que no hay muchas excusas allí.
  7. 1

    x:bind (también llamado «compilado de enlaces de datos») para XAML (aplicación universal) en windows 10 y windows phone 10 de mayo de resolver este problema, consulte https://channel9.msdn.com/Events/Build/2015/3-635

    No puedo encontrar la en línea de google docs, pero no han puesto mucho esfuerzo en el, ya que es algo que no va a utilizar durante algún tiempo. Sin embargo, esta respuesta debe ser un útil puntero a otras personas.

Dejar respuesta

Please enter your comment!
Please enter your name here