自从我上次写到关于标记扩展的时候已经有一段时间了...... Visual Studio 11 Developer Preview的发布给WPF带来了一些新功能,让我有理由再次使用它们。我要在这里讨论的功能可能不是最令人印象深刻的,但它填补了以前版本的空白:支持事件标记扩展。

到目前为止,可以在XAML中使用标记扩展来为属性赋值,但我们无法做到这一点来订阅事件。在WPF 4.5中,现在有可能。所以这里是我们可以用它做的一个小例子...

当使用MVVM模式时,我们通常通过绑定机制将ViewModel的命令与视图的控件关联起来。这种方法通常运行良好,但它有一些缺点:

  • 它在ViewModel中引入了很多样板代码
  • 并不是所有的控件都有一个Command属性(实际上大多数不是),当这个属性存在时,它只对应于控件的一个事件(例如点击一个按钮)。没有真正简单的方法将其他事件“绑定”到ViewModel的命令

能够直接将事件绑定到ViewModel方法会很好,如下所示:

1
2
<Button Content="Click me"
        Click="{my:EventBinding OnClick}" />

使用OnClickViewModel中定义的方法:

1
2
3
4
public void OnClick(object sender, EventArgs e)
{
    MessageBox.Show("Hello world!");
}

那么,现在可以!这是一个概念证明...下面EventBindingExtension显示的类首先获取DataContext控件,然后在该对象上查找指定的方法DataContext,并最终返回此方法的委托:

1
2
3
4
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
三十
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Windows;
using System.Windows.Markup;
    public class EventBindingExtension : MarkupExtension
    {
        public EventBindingExtension() { }
        public EventBindingExtension(string eventHandlerName)
        {
            this.EventHandlerName = eventHandlerName;
        }
        [ConstructorArgument("eventHandlerName")]
        public string EventHandlerName { get; set; }
        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            if (string.IsNullOrEmpty(EventHandlerName))
                throw new ArgumentException("The EventHandlerName property is not set", "EventHandlerName");
            var target = (IProvideValueTarget)serviceProvider.GetService(typeof(IProvideValueTarget));
            EventInfo eventInfo = target.TargetProperty as EventInfo;
            if (eventInfo == null)
                throw new InvalidOperationException("The target property must be an event");
             
            object dataContext = GetDataContext(target.TargetObject);
            if (dataContext == null)
                throw new InvalidOperationException("No DataContext found");
            var handler = GetHandler(dataContext, eventInfo, EventHandlerName);
            if (handler == null)
                throw new ArgumentException("No valid event handler was found", "EventHandlerName");
            return handler;
        }
        #region Helper methods
        static object GetHandler(object dataContext, EventInfo eventInfo, string eventHandlerName)
        {
            Type dcType = dataContext.GetType();
            var method = dcType.GetMethod(
                eventHandlerName,
                GetParameterTypes(eventInfo));
            if (method != null)
            {
                if (method.IsStatic)
                    return Delegate.CreateDelegate(eventInfo.EventHandlerType, method);
                else
                    return Delegate.CreateDelegate(eventInfo.EventHandlerType, dataContext, method);
            }
            return null;
        }
        static Type[] GetParameterTypes(EventInfo eventInfo)
        {
            var invokeMethod = eventInfo.EventHandlerType.GetMethod("Invoke");
            return invokeMethod.GetParameters().Select(p => p.ParameterType).ToArray();
        }
        static object GetDataContext(object target)
        {
            var depObj = target as DependencyObject;
            if (depObj == null)
                return null;
            return depObj.GetValue(FrameworkElement.DataContextProperty)
                ?? depObj.GetValue(FrameworkContentElement.DataContextProperty);
        }
        #endregion
    }

这个类可以像上面例子中所示的那样使用。

现在,这个标记扩展有一个令人讨厌的限制:DataContext必须在调用之前设置ProvideValue,否则将无法找到事件处理程序方法。一种解决方案可能是订阅DataContextChanged事件以在DataContext设置后查找方法,但同时我们仍然需要返回一些内容......并且我们不能返回null,因为它会导致异常(因为您无法订阅具有空处理程序的事件)。所以我们需要返回一个从事件签名中动态生成的假处理程序。它让事情变得更加困难......但它仍然是可行的。

这是实现这种改进的第二个版本:

1
2
3
4
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
三十
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Windows;
using System.Windows.Markup;
    public class EventBindingExtension : MarkupExtension
    {
        private EventInfo _eventInfo;
        public EventBindingExtension() { }
        public EventBindingExtension(string eventHandlerName)
        {
            this.EventHandlerName = eventHandlerName;
        }
        [ConstructorArgument("eventHandlerName")]
        public string EventHandlerName { get; set; }
        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            if (string.IsNullOrEmpty(EventHandlerName))
                throw new ArgumentException("The EventHandlerName property is not set", "EventHandlerName");
            var target = (IProvideValueTarget)serviceProvider.GetService(typeof(IProvideValueTarget));
            var targetObj = target.TargetObject as DependencyObject;
            if (targetObj == null)
                throw new InvalidOperationException("The target object must be a DependencyObject");
            _eventInfo = target.TargetProperty as EventInfo;
            if (_eventInfo == null)
                throw new InvalidOperationException("The target property must be an event");
            object dataContext = GetDataContext(targetObj);
            if (dataContext == null)
            {
                SubscribeToDataContextChanged(targetObj);
                return GetDummyHandler(_eventInfo.EventHandlerType);
            }
            var handler = GetHandler(dataContext, _eventInfo, EventHandlerName);
            if (handler == null)
            {
                Trace.TraceError(
                    "EventBinding: no suitable method named '{0}' found in type '{1}' to handle event '{2'}",
                    EventHandlerName,
                    dataContext.GetType(),
                    _eventInfo);
                return GetDummyHandler(_eventInfo.EventHandlerType);
            }
            return handler;
             
        }
        #region Helper methods
        static Delegate GetHandler(object dataContext, EventInfo eventInfo, string eventHandlerName)
        {
            Type dcType = dataContext.GetType();
            var method = dcType.GetMethod(
                eventHandlerName,
                GetParameterTypes(eventInfo.EventHandlerType));
            if (method != null)
            {
                if (method.IsStatic)
                    return Delegate.CreateDelegate(eventInfo.EventHandlerType, method);
                else
                    return Delegate.CreateDelegate(eventInfo.EventHandlerType, dataContext, method);
            }
            return null;
        }
        static Type[] GetParameterTypes(Type delegateType)
        {
            var invokeMethod = delegateType.GetMethod("Invoke");
            return invokeMethod.GetParameters().Select(p => p.ParameterType).ToArray();
        }
        static object GetDataContext(DependencyObject target)
        {
            return target.GetValue(FrameworkElement.DataContextProperty)
                ?? target.GetValue(FrameworkContentElement.DataContextProperty);
        }
        static readonly Dictionary<Type, Delegate> _dummyHandlers = new Dictionary<Type, Delegate>();
        static Delegate GetDummyHandler(Type eventHandlerType)
        {
            Delegate handler;
            if (!_dummyHandlers.TryGetValue(eventHandlerType, out handler))
            {
                handler = CreateDummyHandler(eventHandlerType);
                _dummyHandlers[eventHandlerType] = handler;
            }
            return handler;
        }
        static Delegate CreateDummyHandler(Type eventHandlerType)
        {
            var parameterTypes = GetParameterTypes(eventHandlerType);
            var returnType = eventHandlerType.GetMethod("Invoke").ReturnType;
            var dm = new DynamicMethod("DummyHandler", returnType, parameterTypes);
            var il = dm.GetILGenerator();
            if (returnType != typeof(void))
            {
                if (returnType.IsValueType)
                {
                    var local = il.DeclareLocal(returnType);
                    il.Emit(OpCodes.Ldloca_S, local);
                    il.Emit(OpCodes.Initobj, returnType);
                    il.Emit(OpCodes.Ldloc_0);
                }
                else
                {
                    il.Emit(OpCodes.Ldnull);
                }
            }
            il.Emit(OpCodes.Ret);
            return dm.CreateDelegate(eventHandlerType);
        }
        private void SubscribeToDataContextChanged(DependencyObject targetObj)
        {
            DependencyPropertyDescriptor
                .FromProperty(FrameworkElement.DataContextProperty, targetObj.GetType())
                .AddValueChanged(targetObj, TargetObject_DataContextChanged);
        }
        private void UnsubscribeFromDataContextChanged(DependencyObject targetObj)
        {
            DependencyPropertyDescriptor
                .FromProperty(FrameworkElement.DataContextProperty, targetObj.GetType())
                .RemoveValueChanged(targetObj, TargetObject_DataContextChanged);
        }
        private void TargetObject_DataContextChanged(object sender, EventArgs e)
        {
            DependencyObject targetObj = sender as DependencyObject;
            if (targetObj == null)
                return;
            object dataContext = GetDataContext(targetObj);
            if (dataContext == null)
                return;
            var handler = GetHandler(dataContext, _eventInfo, EventHandlerName);
            if (handler != null)
            {
                _eventInfo.AddEventHandler(targetObj, handler);
            }
            UnsubscribeFromDataContextChanged(targetObj);
        }
        #endregion
    }

所以这是我们可以做的事情,这要感谢这个新的WPF功能。我们也可以设想一个行为系统,类似于我们可以对附加属性进行的操作,例如在事件发生时执行标准操作。有很多可能的应用程序,我把它留给你找到它们.

from:https://www.thomaslevesque.com/2011/09/23/wpf-4-5-subscribing-to-an-event-using-a-markup-extension/

转载于:https://www.cnblogs.com/Chary/p/No0000130.html

[No0000130]WPF 4.5使用标记扩展订阅事件相关推荐

  1. WPF - 自定义标记扩展

    在使用WPF进行编程的过程中,我们常常需要使用XAML的标记扩展:{Binding},{x:Null}等等.那么为什么WPF提供了XAML标记扩展这一功能,我们又如何创建自定义的标记扩展呢.这就是本文 ...

  2. wpf中xaml的类型转换器与标记扩展

    wpf中xaml的类型转换器与标记扩展 原文:wpf中xaml的类型转换器与标记扩展 这篇来讲wpf控件属性的类型转换器 类型转换器 类型转换器在asp.net控件中已经有使用过了,由于wpf的界面是 ...

  3. 2.6 wpf标记扩展

    1.什么是标记扩展?为什么要有标记扩展? 标记扩展是扩展xmal的表达能力 为了克服现存的类型转换机制存在的 常用的标记扩展有如下: x:Array 代表一个.net数组,它的子元素都是数组元素.它必 ...

  4. WPF学习:4.类型转换和标记扩展

    在上一章,主要介绍了Border和Brush,这一章主要介绍下类型转换和标记扩展.相关代码链接如下: http://files.cnblogs.com/keylei203/4.WPFSampleDem ...

  5. X命名空间-标记扩展

    标记扩展一般含有 {} 符号 x:Type 传入操作数据类型 x:Null 空值 通过例子看下具体: (前台XAML代码) <Window x:Class="WpfApplicatio ...

  6. 第十章:XAML标记扩展(二)

    访问静态成员 IMarkupExtension最简单和最有用的实现之一封装在StaticExtension类中. 这是原始XAML规范的一部分,因此它通常出现在带有x前缀的XAML中. StaticE ...

  7. WPF 3D模型的一个扩展方法

    原文:WPF 3D模型的一个扩展方法 在WPF 3D中,我们常常需要改变一个ModelVisual3D对象的颜色. 先说说ModelVisual3D,本质上3D模型都是由一个个的三角形构成的,并且经过 ...

  8. 第十章:XAML标记扩展(三)

    资源词典 Xamarin.Forms还支持第二种共享对象和值的方法,虽然这种方法比x:静态标记扩展稍微有点开销,但它更通用 - 因为所有东西 - 共享对象和使用的可视元素 它们 - 可以用XAML表示 ...

  9. C# WPF MVVM模式Caliburn.Micro框架下事件发布与订阅

    01 - 前言 处理同模块不同窗体之间的通信和不同模块之间不同窗体的通信,Caliburn提供了一种事件机制,可以在应用程序中低耦合的模块之间进行通信,该机制基于事件聚合器服务,允许发布者和订阅者之间 ...

最新文章

  1. gradle风格的groovy代码
  2. MQTT client id重复导致连接失败
  3. Neutron 架构 - 每天5分钟玩转 OpenStack(67)
  4. raspberry pi_在Raspberry Pi上使用TensorFlow进行对象检测
  5. wxGlade的图标,原来是来自蒙德里安的名画!
  6. CF1342E. Placing Rooks
  7. Spring Boot笔记-接收参数的3种情况
  8. 编译vuejs html,VueJs(2)---VueJs开发环境的搭建和讲解index.html如何被渲染
  9. 假设以带头结点的循环链表表示队列_数据结构·链表(C实现)
  10. Matlab中矩阵编号方式以及一维二维三维数据间的相互转换
  11. matlab制作萨克斯音乐,Cmusic Productions SAXBAND Soprano Sax KONTAKT 萨克斯
  12. C# 值类型和引用类型
  13. Windows2012服务器无法复制粘贴问题
  14. 计算机图形学 dda,计算机图形DDA算法
  15. Python制作2048小游戏
  16. 【资源分享新方式】基于IPv6+Windows的共享文件夹,从此告别第三方云盘
  17. DOTA2怎么清除缓存_第36期 只要一招:彻底重置Windows图标缓存
  18. Python + Scrapy 小小爬虫有大大梦想
  19. 凸集学习——理解凸集概念、凸包演示
  20. 【Python 测验 01编程】数值运算

热门文章

  1. spark rdd reduceByKey示例
  2. Scala集合List的常用方法:take/flatMap/filter/zip/union/intersect/diff及WordCount集合实现
  3. zuul过滤器执行生命周期
  4. rest服务调用方式实现eureka服务实例的增删改查
  5. 小米手机系统服务组件是干什么的_怎么查看小米手机MIUI系统的基本功能-小米手机MIUI系统基础功能查询方法讲解...
  6. 程序员生存定律--程序人生的出口
  7. 记录前端浏览器常见错误SyntaxErro或GET http://xxx/xxx (Not Found)等
  8. 1.1.2 操作系统的特征(并发、共享、虚拟、异步)
  9. python安装email模块_Python使用SMTP模块、email模块发送邮件
  10. linux 使用jstack_案例解析:线程池使用不当导致的系统崩溃