Xác định 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; }
}
Và sử dụng ExposedPropertyBinder
này tôi chỉ viết cho bạn :)
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; }
}
}
Wire nó lên đến Caliburn.Micro của ViewModelBinder
như thế này:
ViewModelBinder.HandleUnmatchedElements = ExposedPropertyBinder.BindElements;
Và thì đấy!
Trang trí tính ViewModel của bạn với 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; }
}
Và liên kết với tài sản của bạn:
<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 ;)
REMARK: Điều này làm việc cho các ví dụ đơn giản của tôi, nhưng điều này chưa được thử nghiệm rộng rãi vì vậy hãy sử dụng nó cẩn thận.
Hy vọng nó phù hợp với bạn! :)
EDIT: Một phiên bản sửa đổi một chút bây giờ có thể được tìm thấy trên GitHub và NuGet
Đó là khá mát mẻ. Cảm ơn! Nó sẽ được một thời gian trước khi tôi nhận được một cơ hội tốt để thử nghiệm này mặc dù. – kasperhj