Читайте также:
|
|
Как было сказано ранее, так как создание экземпляров моделей представления происходит посредством IoC контейнера, возможно расширить логику создания объекта и дополнять его произвольными аспектами.
Данная функциональность недоступна в базовой версии MEF, и требует использования дополнительных библиотек, MefContrib и DynamicProxy (которые помимо перехвата создания объекта реализует некоторые другие полезные функции, выходящие за рамки лекции).
В целях лучшей расширяемости приложения следует объявить перечисление со всеми доступными аспектами (на текущий момент с 1 аспектом):
1: [Flags]
2: public enum Aspects
3: {
4: NotifyPropertyChanged = 1,
5: }
А также необходимо написать небольшой метод-расширение, добавляющий к контейнеру MEF логику перехвата создания объектов. Если в метаданных создаваемого экземпляра найден атрибут AspectMetadata и указан аспект NotifyPropertyChanged, он будет обернут прокси-объектом, реализующим логику INotifyPropertyChanged:
1: public static class AopExtensions
2: {
3: public const string AspectMetadata = "AspectMetadata";
4:
5: public static InterceptionConfiguration AddAopInterception(
6: this InterceptionConfiguration interceptionConfiguration)
7: {
8: return interceptionConfiguration
9:.AddInterceptionCriteria(
10: new PredicateInterceptionCriteria(
11: new PropertyChangedDynamicProxyExportInterceptor(),
12: def => def.ExportDefinitions.Any(export =>
13: export.Metadata.ContainsKey(AspectMetadata) &&
14: ((Aspects)export.Metadata[AspectMetadata] &
15: Aspects.NotifyPropertyChanged)!= 0)));
16: }
17: }
1: internal class PropertyChangedDynamicProxyExportInterceptor:
2: IExportedValueInterceptor
3: {
4: private static readonly ProxyGenerator Generator =
5: new ProxyGenerator();
6: private readonly Type[] _additionalInterfaces = new[]
7: { typeof(INotifyPropertyChanged) };
8:
9: public object Intercept(object value)
10: {
11: object proxy = Generator.CreateClassProxy(
12: value.GetType(), _additionalInterfaces,
13: new[] { new PropertyChangedInterceptor() });
14:
15: Type currentType = value.GetType();
16:
17: do
18: {
19: FieldInfo[] fields =
20: currentType.GetFields(BindingFlags.Instance |
21: BindingFlags.NonPublic | BindingFlags.Public)
22:.Where(field =>
23: (field.Attributes & FieldAttributes.InitOnly) == 0)
24:.ToArray();
25:
26: fields.Select(field => new { Field = field,
27: Value = field.GetValue(value) })
28:.ForEach(desc =>
29: desc.Field.SetValue(proxy, desc.Value));
30:
31: currentType = currentType.BaseType;
32: } while (currentType!= null);
33:
34: return proxy;
35: }
36: }
1: internal class PropertyChangedInterceptor: IInterceptor
2: {
3: private event PropertyChangedEventHandler PropertyChanged;
4:
5: public void Intercept(IInvocation invocation)
6: {
7: string methodName = invocation.Method.Name;
8:
9: if (invocation.Method.IsSpecialName)
10: {
11: if (invocation.Method.DeclaringType ==
12: typeof(INotifyPropertyChanged))
13: {
14: if (methodName.StartsWith("add_"))
15: {
16: PropertyChanged+=(PropertyChangedEventHandler)
17: invocation.Arguments[0];
18: }
19: else
20: {
21: PropertyChanged-=(PropertyChangedEventHandler)
22: invocation.Arguments[0];
23: }
24:
25: return;
26: }
27:
28: if (methodName.StartsWith("set_"))
29: {
30: PropertyInfo propertyInfo = invocation.Proxy
31:.GetType().GetProperty(methodName.Substring(4));
32: object oldValue = propertyInfo.GetValue(
33: invocation.Proxy,
34: invocation.Arguments.SkipLast(1).ToArray());
35:
36: invocation.Proceed();
37:
38: if (oldValue == invocation.Arguments
39: [invocation.Arguments.Length - 1])
40: {
41: return;
42: }
43:
44: OnPropertyChanged(invocation.Proxy,
45: new PropertyChangedEventArgs(propertyInfo.Name));
46:
47: return;
48: }
49: }
50:
51: invocation.Proceed();
52: }
53:
54: private void OnPropertyChanged(object sender,
55: PropertyChangedEventArgs e)
56: {
57: PropertyChangedEventHandler eventHandler=PropertyChanged;
58:
59: if (eventHandler!= null)
60: {
61: eventHandler(sender, e);
62: }
63: }
64: }
Последним шагом является вызов метода-расширения для MEF контекста приложения:
1: var catalog = new InterceptingCatalog(new DirectoryCatalog("."),
2: new InterceptionConfiguration().AddAopInterception());
Теперь любой класс, помеченный атрибутом [ExportMetadata(AopExtensions.AspectMetadata, Aspects.NotifyPropertyChanged)], при разрешении через IoC контейнер, автоматически получит реализацию INotifyPropertyChanged для своих виртуальных свойств.
Дата добавления: 2015-08-13; просмотров: 76 | Нарушение авторских прав
<== предыдущая страница | | | следующая страница ==> |
Сопоставление модели представления и представления | | | Краткие итоги |