Bienvenue à Blogs CodeS-SourceS Identification | Inscription | Aide

Abonnements

SL5 : ICustomTypeProvider

On a souvent le problème suivant avec SL4 : dans le Model on récupère une collection d’entités et dans le ViewModel, on veut récupérer cette collection en y ajoutant de l’intelligence (par exemple des propriétés calculées).

Dans ce cas, il faut gérer deux collections et propager les modifications dans les deux sens. Une bonne pratique consiste à utiliser des "RelayCollection" pour s’occuper de cette propagation.

En .NET, on peut ajouter de "fausses" propriétés utilisées pour le binding en implémentant l’interface ICustomTypeDescriptor mais celle-ci n’existe pas dans Silverlight.

Cependant, avec SL5, on peut utiliser l’interface ICustomTypeProvider.

J’ai repris le code d’Alexandra et j’ai tenté de l’améliorer.

Cela nous donne le code suivant :

public abstract class DynamicBaseType
{
   
public abstract object GetPropertyValue(string
propertyName);
   
public abstract void SetPropertyValue(string propertyName, object
value);
}
public abstract class DynamicBaseType<T> : DynamicBaseType, ICustomTypeProvider, INotifyPropertyChanged
    where T : DynamicBaseType
<T> {
   
private static List<CustomPropertyInfo> _customProperties = new List<CustomPropertyInfo
>();
   
private Dictionary<string, object
> _customPropertyValues;
   
private CustomType
_customtype;

   
protected
DynamicBaseType()
    {
        _customPropertyValues =
new Dictionary<string, object
>();
       
foreach (var property in
_customProperties)
            _customPropertyValues.Add(property.Name,
null
);
    }

   
public static void AddProperty(string name, Type type, object value = null, List<Attribute> attributes = null
)
    {
       
if
(!CheckIfNameExists(name))
            _customProperties.Add(
new CustomPropertyInfo
(name, type, value, attributes));
    }

   
public static void AddProperty<V>(string name, Func<T, V> get, Action<T, V> set = null, List<Attribute> attributes = null, string[] properties = null
)
    {
       
if
(!CheckIfNameExists(name))
            _customProperties.Add(
new CustomPropertyInfo
<V>(name, get, set, attributes, properties));
    }

   
private static bool CheckIfNameExists(string
name)
    {
       
if (_customProperties.Select(p => p.Name).Contains(name) || typeof
(T).GetProperties().Select(p => p.Name).Contains(name))
           
throw new Exception("The property with this name already exists: "
+ name);
       
return false
;
    }

   
private bool ValidateValueType(object value, Type
type)
    {
       
if (value == null
)
        {
           
if
(!type.IsValueType)
               
return true
;
           
return (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable
<>));
        }
       
return
type.IsAssignableFrom(value.GetType());
    }

   
public override object GetPropertyValue(string
propertyName)
    {
       
object
customPropertyValue;
       
if (_customPropertyValues.TryGetValue(propertyName, out
customPropertyValue))
           
return customPropertyValue ?? _customProperties.First(p => p.Name == propertyName).GetDefaultValue(this
);
       
throw new Exception("There is no property "
+ propertyName);
    }

   
public override void SetPropertyValue(string propertyName, object
value)
    {
       
CustomPropertyInfo
propertyInfo = _customProperties.FirstOrDefault(prop => prop.Name == propertyName);
       
object
customPropertyValue;
       
if (!_customPropertyValues.TryGetValue(propertyName, out
customPropertyValue))
           
throw new Exception("There is no property "
+ propertyName);
       
if
(ValidateValueType(value, propertyInfo.PropertyType))
        {
           
if
(customPropertyValue != value)
            {
                _customPropertyValues[propertyName] = value;
                OnPropertyChanged(propertyName);
            }
        }
       
else throw new Exception("Value is of the wrong type or null for a non-nullable type."
);
    }

   
public PropertyInfo
[] GetProperties()
    {
       
return this
.GetCustomType().GetProperties();
    }

   
public Type
GetCustomType()
    {
       
return _customtype ?? (_customtype = new CustomType(typeof
(T)));
    }

   
protected virtual void OnPropertyChanged(string
propertyName)
    {
       
if (PropertyChanged != null
)
        {
            PropertyChanged(
this, new PropertyChangedEventArgs
(propertyName));
           
foreach (var dependantCustomPropertyInfo in _customProperties.OfType<IDependantCustomPropertyInfo
>().Where(dcpi => dcpi.Properties.Contains(propertyName)))
                PropertyChanged(
this, new PropertyChangedEventArgs
(dependantCustomPropertyInfo.Name));
        }
    }
   
public event PropertyChangedEventHandler
PropertyChanged;

   
private class CustomType : Type
    {
       
Type
_baseType;
       
public CustomType(Type
delegatingType)
        {
            _baseType = delegatingType;
        }
       
public override Assembly
Assembly
        {
           
get { return
_baseType.Assembly; }
        }

       
public override string
AssemblyQualifiedName
        {
           
get { return
_baseType.AssemblyQualifiedName; }
        }

       
public override Type
BaseType
        {
           
get { return
_baseType.BaseType; }
        }

       
public override string
FullName
        {
           
get { return
_baseType.FullName; }
        }

       
public override Guid
GUID
        {
           
get { return
_baseType.GUID; }
        }

       
protected override TypeAttributes
GetAttributeFlagsImpl()
        {
           
throw new NotImplementedException
();
        }

       
protected override ConstructorInfo GetConstructorImpl(BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier
[] modifiers)
        {
           
throw new NotImplementedException
();
        }

       
public override ConstructorInfo[] GetConstructors(BindingFlags
bindingAttr)
        {
           
return
_baseType.GetConstructors(bindingAttr);
        }

       
public override Type
GetElementType()
        {
           
return
_baseType.GetElementType();
        }

       
public override EventInfo GetEvent(string name, BindingFlags
bindingAttr)
        {
           
return
_baseType.GetEvent(name, bindingAttr);
        }

       
public override EventInfo[] GetEvents(BindingFlags
bindingAttr)
        {
           
return
_baseType.GetEvents(bindingAttr);
        }

       
public override FieldInfo GetField(string name, BindingFlags
bindingAttr)
        {
           
return
_baseType.GetField(name, bindingAttr);
        }

       
public override FieldInfo[] GetFields(BindingFlags
bindingAttr)
        {
           
return
_baseType.GetFields(bindingAttr);
        }

       
public override Type GetInterface(string name, bool
ignoreCase)
        {
           
return
_baseType.GetInterface(name, ignoreCase);
        }

       
public override Type
[] GetInterfaces()
        {
           
return
_baseType.GetInterfaces();
        }

       
public override MemberInfo[] GetMembers(BindingFlags
bindingAttr)
        {
           
return
_baseType.GetMembers(bindingAttr);
        }

       
protected override MethodInfo GetMethodImpl(string name, BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier
[] modifiers)
        {
           
throw new NotImplementedException
();
        }

       
public override MethodInfo[] GetMethods(BindingFlags
bindingAttr)
        {
           
return
_baseType.GetMethods(bindingAttr);
        }

       
public override Type GetNestedType(string name, BindingFlags
bindingAttr)
        {
           
return
_baseType.GetNestedType(name, bindingAttr);
        }

       
public override Type[] GetNestedTypes(BindingFlags
bindingAttr)
        {
           
return
_baseType.GetNestedTypes(bindingAttr);
        }

       
public override PropertyInfo[] GetProperties(BindingFlags
bindingAttr)
        {
           
PropertyInfo
[] clrProperties = _baseType.GetProperties(bindingAttr);
           
if (clrProperties != null
)
               
return
clrProperties.Concat(_customProperties).ToArray();
           
return
_customProperties.ToArray();
        }

       
protected override PropertyInfo GetPropertyImpl(string name, BindingFlags bindingAttr, Binder binder, Type returnType, Type[] types, ParameterModifier
[] modifiers)
        {
           
return
GetProperties(bindingAttr).FirstOrDefault(prop => prop.Name == name) ?? _customProperties.FirstOrDefault(prop => prop.Name == name);
        }

       
protected override bool
HasElementTypeImpl()
        {
           
throw new NotImplementedException
();
        }

       
public override object InvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target, object[] args, ParameterModifier[] modifiers, System.Globalization.CultureInfo culture, string
[] namedParameters)
        {
           
return
_baseType.InvokeMember(name, invokeAttr, binder, target, args, modifiers, culture, namedParameters);
        }

       
protected override bool
IsArrayImpl()
        {
           
throw new NotImplementedException
();
        }

       
protected override bool
IsByRefImpl()
        {
           
throw new NotImplementedException
();
        }

       
protected override bool
IsCOMObjectImpl()
        {
           
throw new NotImplementedException
();
        }

       
protected override bool
IsPointerImpl()
        {
           
throw new NotImplementedException
();
        }

       
protected override bool
IsPrimitiveImpl()
        {
           
return
_baseType.IsPrimitive;
        }

       
public override Module
Module
        {
           
get { return
_baseType.Module; }
        }

       
public override string
Namespace
        {
           
get { return
_baseType.Namespace; }
        }

       
public override Type
UnderlyingSystemType
        {
           
get { return
_baseType.UnderlyingSystemType; }
        }

       
public override object[] GetCustomAttributes(Type attributeType, bool
inherit)
        {
           
return
_baseType.GetCustomAttributes(attributeType, inherit);
        }

       
public override object[] GetCustomAttributes(bool
inherit)
        {
           
return
_baseType.GetCustomAttributes(inherit);
        }

       
public override bool IsDefined(Type attributeType, bool
inherit)
        {
           
return
_baseType.IsDefined(attributeType, inherit);
        }

       
public override string
Name
        {
           
get { return
_baseType.Name; }
        }
    }

   
private class CustomPropertyInfo : PropertyInfo
    {
       
private string
_name;
       
private Type
_type;
       
private object
_defaultValue;
       
private List<Attribute
> _attributes;

       
public CustomPropertyInfo(string name, Type type, object defaultValue = null, List<Attribute> attributes = null
)
            :
this
(name, type, attributes)
        {
            _defaultValue = defaultValue;
        }

       
protected CustomPropertyInfo(string name, Type type, List<Attribute> attributes = null
)
        {
            _name = name;
            _type = type;
            _attributes = attributes;
        }

       
public virtual object GetDefaultValue(DynamicBaseType
entity)
        {
           
return
_defaultValue;
        }

       
public override PropertyAttributes
Attributes
        {
           
get { throw new NotImplementedException
(); }
        }

       
public override bool
CanRead
        {
           
get { return true
; }
        }

       
public override bool
CanWrite
        {
           
get { return true
; }
        }

       
public override MethodInfo[] GetAccessors(bool
nonPublic)
        {
           
throw new NotImplementedException
();
        }

       
public override MethodInfo GetGetMethod(bool
nonPublic)
        {
           
throw new NotImplementedException
();
        }

       
public override ParameterInfo
[] GetIndexParameters()
        {
           
throw new NotImplementedException
();
        }

       
public override MethodInfo GetSetMethod(bool
nonPublic)
        {
           
throw new NotImplementedException
();
        }

       
public override object GetValue(object obj, BindingFlags invokeAttr, Binder binder, object[] index, System.Globalization.CultureInfo
culture)
        {
           
return ((DynamicBaseType
)obj).GetPropertyValue(_name);
        }

       
public override void SetValue(object obj, object value, BindingFlags invokeAttr, Binder binder, object[] index, System.Globalization.CultureInfo
culture)
        {
            ((
DynamicBaseType
)obj).SetPropertyValue(_name, value);
        }

       
public override Type
PropertyType
        {
           
get { return
_type; }
        }

       
public override Type
DeclaringType
        {
           
get { throw new NotImplementedException
(); }
        }

       
public override object[] GetCustomAttributes(Type attributeType, bool
inherit)
        {
           
return _attributes == null ? new object
[0] : _attributes.Where(a => a.GetType() == attributeType).ToArray();
        }

       
public override object[] GetCustomAttributes(bool
inherit)
        {
           
return _attributes == null ? new object
[0] : _attributes.ToArray();
        }

       
public override bool IsDefined(Type attributeType, bool
inherit)
        {
           
throw new NotImplementedException
();
        }

       
public override string
Name
        {
           
get { return
_name; }
        }

       
public override Type
ReflectedType
        {
           
get { throw new NotImplementedException
(); }
        }
    }

   
private interface IDependantCustomPropertyInfo
    {
       
string Name { get
; }
       
string[] Properties { get
; }
    }

   
private class CustomPropertyInfo<V> : CustomPropertyInfo, IDependantCustomPropertyInfo
    {
       
private Func
<T, V> _get;
       
private Action
<T, V> _set;
       
private string
[] _properties;

       
public CustomPropertyInfo(string name, Func<T, V> get, Action<T, V> set = null, List<Attribute> attributes = null, string[] properties = null
)
            :
base(name, typeof
(V), attributes)
        {
            _get = get;
            _set = set;
            _properties = properties;
        }

       
public string
[] Properties
        {
           
get { return
_properties; }
        }

       
public override object GetDefaultValue(DynamicBaseType
entity)
        {
           
return
_get((T)entity);
        }

       
public override bool
CanWrite
        {
           
get { return _set != null
; }
        }

       
public override object GetValue(object obj, BindingFlags invokeAttr, Binder binder, object[] index, System.Globalization.CultureInfo
culture)
        {
           
return
_get((T)obj);
        }

       
public override void SetValue(object obj, object value, BindingFlags invokeAttr, Binder binder, object[] index, System.Globalization.CultureInfo
culture)
        {
           
if (_set == null
)
               
throw new InvalidOperationException();
            _set((T)obj, (V)value);
        }
    }
}

Que je peux utiliser très facilement.

Avec ma classe Customer par exemple :

public class Customer : DynamicBaseType<Customer>
{
   
private String
firstName;
   
public String
FirstName
    {
       
get
{ return firstName; }
       
set
        {
            firstName =
value
;
            OnPropertyChanged(
"FirstName"
);
        }
    }

   
private String
lastName;
   
public String
LastName
    {
       
get
{ return lastName; }
       
set
        {
            lastName =
value
;
            OnPropertyChanged(
"LastName");
        }
    }
}

Dans le ViewModel, je peux désormais utiliser le code suivant :

Customer.AddProperty("Age", typeof(int));
Customer.AddProperty("Married", typeof(bool));
Customer.AddProperty("FullName", c => string.Format("{0} {1}", c.LastName, c.FirstName), properties:new string[] { "LastName", "FirstName" });
 
customers[0].SetPropertyValue("Age", 40);
customers[0].SetPropertyValue("Married", true);
 
customers[1].SetPropertyValue("Age", 45);
customers[1].SetPropertyValue("Married", true);

Ce qui est nouveau dans ma solution par rapport au code de départ c’est que FullName est calculé à partir de d’autres propriétés. Si on change la propriété LastName ou FirstName d’un Customer, celui-ci lèvera également l’évènement PropertyChanged avec FullName.

Ce post vous a plu ? Ajoutez le dans vos favoris pour ne pas perdre de temps à le retrouver le jour où vous en aurez besoin :

Publié lundi 12 septembre 2011 23:58 par Matthieu MEZIL

Classé sous :

Commentaires

Pas de commentaires

Les commentaires anonymes sont désactivés

Les 10 derniers blogs postés

- Merci par Blog de Jérémy Jeanson le 10-01-2019, 20:47

- Office 365: Script PowerShell pour auditer l’usage des Office Groups de votre tenant par Blog Technique de Romelard Fabrice le 04-26-2019, 11:02

- Office 365: Script PowerShell pour auditer l’usage de Microsoft Teams de votre tenant par Blog Technique de Romelard Fabrice le 04-26-2019, 10:39

- Office 365: Script PowerShell pour auditer l’usage de OneDrive for Business de votre tenant par Blog Technique de Romelard Fabrice le 04-25-2019, 15:13

- Office 365: Script PowerShell pour auditer l’usage de SharePoint Online de votre tenant par Blog Technique de Romelard Fabrice le 02-27-2019, 13:39

- Office 365: Script PowerShell pour auditer l’usage d’Exchange Online de votre tenant par Blog Technique de Romelard Fabrice le 02-25-2019, 15:07

- Office 365: Script PowerShell pour auditer le contenu de son Office 365 Stream Portal par Blog Technique de Romelard Fabrice le 02-21-2019, 17:56

- Office 365: Script PowerShell pour auditer le contenu de son Office 365 Video Portal par Blog Technique de Romelard Fabrice le 02-18-2019, 18:56

- Office 365: Script PowerShell pour extraire les Audit Log basés sur des filtres fournis par Blog Technique de Romelard Fabrice le 01-28-2019, 16:13

- SharePoint Online: Script PowerShell pour désactiver l’Option IRM des sites SPO non autorisés par Blog Technique de Romelard Fabrice le 12-14-2018, 13:01