2012-11-12 3 views

risposta

7

Definire il ExposeAttribute:

[AttributeUsage(AttributeTargets.Property, AllowMultiple = true)] 
public class ExposeAttribute : Attribute 
{ 
    public ExposeAttribute(string propertyName) 
    { 
     PropertyName = propertyName; 
    } 

    public ExposeAttribute(string propertyName, string modelPropertyName) 
    { 
     PropertyName = propertyName; 
     ModelPropertyName = modelPropertyName; 
    } 

    public string PropertyName { get; set; } 

    public string ModelPropertyName { get; set; } 
} 

e utilizzare questa ExposedPropertyBinder ho appena scritto per voi :)

public static class ExposedPropertyBinder 
{ 
    private static readonly ILog Log = LogManager.GetLog(typeof(ExposedPropertyBinder)); 

    public static void BindElements(IEnumerable<FrameworkElement> elements, Type viewModelType) 
    { 
     foreach (var element in elements) 
     { 
      var parts = element.Name.Trim('_') 
       .Split(new[] { '_' }, StringSplitOptions.RemoveEmptyEntries); 

      // Get first exposed property 
      var exposedPropertyInfo = GetExposedPropertyInfo(viewModelType, parts[0]); 
      if (exposedPropertyInfo == null) 
      { 
       Log.Info("Binding Convention Not Applied: Element {0} did not match a property.", element.Name); 
       continue; 
      } 

      var breadCrumb = new List<string> { exposedPropertyInfo.Path }; 

      // Loop over all parts and get exposed properties 
      for (var i = 1; i < parts.Length; i++) 
      { 
       var exposedViewModelType = exposedPropertyInfo.ViewModelType; 

       exposedPropertyInfo = GetExposedPropertyInfo(exposedViewModelType, parts[i]); 
       if (exposedPropertyInfo == null) break; 

       breadCrumb.Add(exposedPropertyInfo.Path); 
      } 

      if (exposedPropertyInfo == null) 
      { 
       Log.Info("Binding Convention Not Applied: Element {0} did not match a property.", element.Name); 
       continue; 
      } 

      var convention = ConventionManager.GetElementConvention(element.GetType()); 
      if (convention == null) 
      { 
       Log.Warn("Binding Convention Not Applied: No conventions configured for {0}.", element.GetType()); 
       continue; 
      } 

      var applied = convention.ApplyBinding(exposedPropertyInfo.ViewModelType, 
       string.Join(".", breadCrumb), exposedPropertyInfo.Property, element, convention); 

      var appliedMessage = string.Format(applied 
       ? "Binding Convention Applied: Element {0}." 
       : "Binding Convention Not Applied: Element {0} has existing binding.", element.Name); 

      Log.Info(appliedMessage); 
     } 
    } 

    private static ExposedPropertyInfo GetExposedPropertyInfo(Type type, string propertyName) 
    { 
     foreach (var property in type.GetProperties()) 
     { 
      if (property.Name.Equals(propertyName, StringComparison.OrdinalIgnoreCase)) 
       return new ExposedPropertyInfo(property.PropertyType, property.Name, property); 

      // Get first ExposeAttribute which matches property name 
      var exposeAttribute = GetExposeAttribute(property, propertyName); 
      if (exposeAttribute == null) continue; 

      // Get the name of the exposed property 
      var exposedPropertyName = exposeAttribute.ModelPropertyName ?? exposeAttribute.PropertyName; 

      var path = string.Join(".", property.Name, exposedPropertyName); 
      var viewModelType = property.PropertyType; 
      var propertyInfo = property; 

      // Check if property exists 
      var exposedProperty = viewModelType.GetPropertyCaseInsensitive(exposedPropertyName); 
      if (exposedProperty == null) 
      { 
       // Do recursive check for exposed properties 
       var child = GetExposedPropertyInfo(viewModelType, exposedPropertyName); 
       if (child == null) continue; 

       path = string.Join(".", property.Name, child.Path); 
       viewModelType = child.ViewModelType; 
       propertyInfo = child.Property; 
      } 

      return new ExposedPropertyInfo(viewModelType, path, propertyInfo); 
     } 

     return null; 
    } 

    private static ExposeAttribute GetExposeAttribute(PropertyInfo property, string propertyName) 
    { 
     return property 
      .GetCustomAttributes(typeof(ExposeAttribute), true) 
      .Cast<ExposeAttribute>() 
      .FirstOrDefault(a => a.PropertyName.Equals(propertyName, StringComparison.OrdinalIgnoreCase)); 
    } 

    private class ExposedPropertyInfo 
    { 
     public ExposedPropertyInfo(Type viewModelType, string path, PropertyInfo property) 
     { 
      ViewModelType = viewModelType; 
      Path = path; 
      Property = property; 
     } 

     public Type ViewModelType { get; private set; } 

     public string Path { get; private set; } 

     public PropertyInfo Property { get; private set; } 
    } 
} 

filo fino a Caliburn.Micro di ViewModelBinder come questo:

ViewModelBinder.HandleUnmatchedElements = ExposedPropertyBinder.BindElements; 

E voilà!

Decora il tuo proprietà ViewModel con la ExposeAttribute:

public class MainViewModel : PropertyChangedBase 
{ 
    private Person _person; 

    [Expose("FirstName")] 
    [Expose("LastName")] 
    [Expose("ZipCode")] 
    public Person Person 
    { 
     get { return _person; } 
     set 
     { 
      _person = value; 
      NotifyOfPropertyChange(() => Person); 
     } 
    } 
} 

public class Person 
{ 
    public string FirstName { get; set; } 

    public string LastName { get; set; } 

    [Expose("ZipCode", "zip_code")] 
    public Address Address { get; set; } 

    public string FullName 
    { 
     get { return string.Join(" ", FirstName, LastName); } 
    } 

    public override string ToString() 
    { 
     return FullName; 
    } 
} 

public class Address 
{ 
    public string zip_code { get; set; } 
} 

e si legano alle proprietà:

<TextBlock x:Name="Person_FullName" /> 
    <TextBlock x:Name="FirstName" /> 
    <TextBlock x:Name="LastName" /> 
    <TextBlock x:Name="ZipCode" />     // 
    <TextBlock x:Name="Person_ZipCode" />   // THESE ARE THE SAME ;) 

NOTA: Questo ha lavorato per i miei semplici esempi, ma questo non è stato ampiamente testato quindi usalo con cura.

Spero che funzioni per voi! :)

EDIT: una versione leggermente modificata ora può essere trovato sulla GitHub e NuGet

+0

Questo è piuttosto fresco. Grazie! Passerà un po 'di tempo prima che abbia una buona possibilità di testarlo. – kasperhj