门户网站主要包括哪些模块,南皮县网站建设价格,360网站托管,wordpress数据库文件在哪里设置1.1 事件概括 第一节中我们给窗体添加了一个按钮#xff0c;不过好像Button点个几下也只有些发光样式的变化#xff0c;什么你还把系统皮肤去掉了#xff1f;算了承认下确实够寒碜#xff0c;那让我们再动动手。 1.1.1 路由事件简述 public HelloWorld() { Button button …1.1 事件概括 第一节中我们给窗体添加了一个按钮不过好像Button点个几下也只有些发光样式的变化什么你还把系统皮肤去掉了算了承认下确实够寒碜那让我们再动动手。 1.1.1 路由事件简述 public HelloWorld() { Button button new Button(); button.AddHandler(Button.ClickEvent,new RoutedEventHandler(button_Click)); …… } void button_Click(object sender, RoutedEventArgs e) { MessageBox.Show(Hello World); } 本例中也可以由来操作如button.Click new RoutedEventHandler(button_Click) 这段功能好像没有什么出奇的就是给button添加了个Click事件效果无非是跳出个Hello World的提示框.可大家有没注意到它的委托字样变了RoutedEventHandlerMS中文定义为路由事件那为什么叫作路由呢要知道路由器我都坏了好几个完全勾起了那段伤痛的回忆(以下省略悲伤3万字)。路由(Router)我们知道是安排线路的人,那么Route是什么意思呢?有人可能已经按捺不住:就是安排线路的意思嘛。完全正确一百分可惜没有任何奖励^-^。 1.1.2 附加事件 在WPF中大部分类派生自UIElement 或 ContentElement如前Button和Window便派生于UIElement ,这些类可以是任一路由事件的事件侦听器, 这个和附加属性有些相似你本身可以没有此事件但要附加的事件必须是先存在的。在上例构造函数中增加这么一句 this.AddHandler(Button.ClickEvent, new RoutedEventHandler(window_Click)); 就是window为它容器内类型为Button或是继承自Button的元素增加了一个点击事件(ClickEvent)而本来Button的点击事件是只有Button或是派生类才有的增加该句后window也可以捕获button的click事件;要问为什么this就是算作窗体因为我们这个类继承自window如果继承自Button那this就代表按钮了而且此事件会对他本身也有效了. 1.1.3 冒泡 如果父容器也可以引发子元素的事件那么有个相同的事件一起引发会是怎么样的结果呢我们不要忘记程序员优良而光荣的传统敲几行代码先。 public HelloWorld() { this.AddHandler(Button.ClickEvent, new RoutedEventHandler(window_Click)); StackPanel parentPanel new StackPanel(); parentPanel.AddHandler(Button.ClickEvent, new RoutedEventHandler(parentPanel_Click)); Button button new Button(); button.AddHandler(Button.ClickEvent, new RoutedEventHandler(button_Click)); button.Name firstButton; button.Content Im a button; parentPanel.Children.Add(button); this.Content parentPanel; } void window_Click(object sender, RoutedEventArgs e) { MessageBox.Show(window); } void parentPanel_Click(object sender, RoutedEventArgs e) { MessageBox.Show(parentPanel); } void button_Click(object sender, RoutedEventArgs e) { MessageBox.Show(button); } 上面这段代码定义了一个StackPanel容器parentPanel并把我们刚才的button按钮放到里面通过 parentPanel.AddHandler(Button.ClickEvent, new RoutedEventHandler(parentPanel_Click)); 为button又订阅了一个事件,让button在点击时弹出一个写有”parentPanel”字样的对话框;Window也订阅了一个相同的事件让其弹出”window”字样的对话框最后一句this.Content parentPanel;是指窗体内容由StackPanel来填充。 三个元素(Window, StackPanel,Button)都分别的对Button这个按钮的点击事件(Click)作了定义而窗体中只有名为button 的一个按钮见(图 2-1)点击它会产生什么效果呢 (图 2-1) 别瞎想了运行点击下不就得了结果是跳出了MessageBox那不是废话嘛关键不是一个是三个 三个弹出框依次是”button”-” parentPanel”-”window”。这样我们可以得出结论系统对Click这个事件的加载顺序是先从我们所见源元素再一层层向上引发的我们称这叫做冒泡(Bubble)。 1.1.4 隧道 既然有从所见层到最外程的概念那有没有从外层到内层的概念呢也就是说”window”-” parentPanel”-”button” 这样的弹出顺序世上阴阳对存无独有偶明显是有的它叫做 隧道(Tunnel)也可以理解为从外层开了条隧道到内部这点在做复合控件的时候比较有用你应该希望得到消息最先是控件本身而不是内部的子控件.隧道又称预览比如WPF的默认输入事件(mouseUp,mouseDown,keyUp)是通过隧道和冒泡结合来绑定同一个参数KeyDown 事件和 PreviewKeyDown 事件具有相同的签名前者是冒泡输入事件后者是隧道输入事件隧道是比冒泡先引发的。(一般Preview带头的是隧道) 事件的处理顺序如下所示 1. 针对根元素(window)处理 PreviewMouseDown隧道。 2. 针对中间元素(stackpanel)处理 PreviewMouseDown隧道。 3. 针对源元素(button)处理 PreviewMouseDown隧道。 4. 针对源元素(button)处理 MouseDown冒泡。 5. 针对中间元素(stackpanel)处理 MouseDown冒泡。 6. 针对根元素(window)处理 MouseDown冒泡。 这里要强调的是隧道优先然后冒泡指的仅仅是WPF里的默认的输入事件如果自定义控件的话先冒泡后隧道还是先隧道后冒泡都是控件开发者自己来决定的其实说白了也就个参数共享和事件调用顺序而已。 1.1.5 直接和模拟类似直接 有的人可能不理解了我就是要点下按钮就是让那个”button”弹出就可以了其他的关我啥事也就是说源元素本身才响应事件,这也是winform中默认定义的事件WPF中称为直接事件(Direct),当然在本例中系统已经定义Click的事件为冒泡(Bubble)那该怎么做呢难道就束手无策了,让对话框只能接二连三的弹出MS当然不可能没有考虑到这点你只要在button事件中添加e.Handled true;这句如 void button_Click(object sender, RoutedEventArgs e) { MessageBox.Show(button); e.Handled true; } 这样的话” parentPanel”,”window”对话框就弹不出来了话音未落只见又有人提出了更为苛刻的需求我只要求button和window弹出对话框该怎么办 当然可以,事实上事件的原理就是把函数指针放入到一个队列中出队无论如何都是要轮一遍加入到队列中的事件e.Handled true;并没有把事件队列给停止,打个比方你去排队买票已经有2个人在你前面(你们去的是同一地)无论他们是否买的到票这年月买票也不容易除有人中途离开被RemoveHanlded正常情况下你都要等两人去窗台询问是否有票可以买后才能被轮到询问过程就是e.Handled如果被定为true说明票已经卖完那么按照逻辑剩下的人应该是无法买到票的可假定我事前就有预定不管前面的人是否买到票也能拿票回家。 那怎么预定票呢?在程序中我们只需要把window的事件在增加的时候就定义为必须触发就可以了this.AddHandler(Button.ClickEvent, new RoutedEventHandler(window_Click),true);第三个参数handleEventsToo设置为true即可.如果设为false在e.Handled true;的时候就不引发. handleEventsToo方法适用于冒泡(Bubble)和隧道(Tunnel)局限性在于不能在 XAML 中像使用属性那么定义只能通过后端程序(CLR)来注册。 1.1.6 如何自定义事件 说了这么多想必你也想自己定义个事件试试下面我们借用MSDN的例子自定义一个继承自Button的控件MyButtonSimple. public class MyButtonSimple : Button { //创建自定义路由事件的第一步便是用EventManager先注册一个此例用的是冒泡事件 public static readonly RoutedEvent TapEvent EventManager.RegisterRoutedEvent( Tap, RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(MyButtonSimple)); //Clr的属性声明对于XAML该属性只是为了编译通过 public event RoutedEventHandler Tap { add { AddHandler(TapEvent, value); } remove { RemoveHandler(TapEvent, value); } } //引发事件 void RaiseTapEvent() { RoutedEventArgs newEventArgs new RoutedEventArgs(MyButtonSimple.TapEvent); RaiseEvent(newEventArgs); } //重载onclick事件的具体实现, 当点击按钮的时候便能引发我们自定义的Tap事件 protected override void OnClick() { RaiseTapEvent(); } } 其中引发事件我们之前的做法可能是 if (Tap ! null) Tap(this, new RoutedEventArgs()); 但如今因为我们定义的事件是放在一个Dictionary容器中外部无法操控所以只能运用系统的RaiseEvent函数进行引发事件操作,不过最让人心烦的是RaiseEvent中的参数只接受RoutedEventArgs类型这就意味着我们不能自定义多个参数的委托多参数必须是派生自RoutedEventArgs类后通过增加属性来实现例如Button的Drag事件, public delegate void DragEventHandler(object sender, DragEventArgs e);所以只能老老实实的和MS的做法一样定义一个为object类型的事件发出者和一个继承自RoutedEventArgs的参数e。注DragEventArgs派生于RoutedEventArgs。 本节的示例程序有window,stackpanel,button多个元素引发事件怎样才能找出源元素 void HandleClick(object sender, RoutedEventArgs e) { // 这里的sender如果是window引发的则是window当然Button引发的便是Button Button srcButton e.Source as Button;//原始引发者 srcButton.Width 200; } 1.1.7 为路由事件添加类处理 在冒泡示例中只定义了一个stackpanel假如我们现在走极端需要声明一百个stackpanel类每个类都需要为子元素的Button弹出一个对话框难道只能每次在stackpanel声明后增加一行对应的语句来实现有没有种更快捷的方法如对stackpanel类型一次性注册点击事件。在WPF中可以用EventManager.RegisterClassHandler来注册我们只需要在静态构造函数中这样定义 static HelloWorld() { EventManager.RegisterClassHandler(typeof(Button), Button.ClickEvent, new RoutedEventHandler(button_Click)); } 这样定义的一个缺点 是一旦你注册了你使用到的此类型都会注册这个事件而问题还在于无法把此注册删除除非一个个删除至少我没发现捷径-_-!。 如果基类和子类均注册了类处理功能则将首先调用子类的处理程序。 1.1.8 弱事件 WeakEvent 在讲此之前让我们先来看一个这样的例子: public delegate void MessageWriter(string text); public class EventClass { public event MessageWriter DoEvent; public void ConsoleOut(string text) { if (DoEvent!null) DoEvent(text); } } public class ConsoleWriter { public void WriteToConsole(string text) { Console.Write(text); } } // Client code... ConsoleWriter writerObj new ConsoleWriter(); EventClass evetObj new EventClass(); evetObj.DoEvent new MessageWriter(writerObj.WriteToConsole); //writerObj null; //GC.Collect(); evetObj.ConsoleOut(Hello, World!); 这段代码应该没有什么难理解的地方,只是演示了一个类(EventClass)中的事件委托MessageWriter由另一个类(ConsoleWriter)的WriteToConsole函数来实现.关键在于被注释掉的这句 writerObj null; GC.Collect(); 把注释去掉的话,我们希望程序是运行错误的,因为对象已经被删除了,事件中的委托理应跟随着对象的删除而消失,可问题是去掉注释后,我们仍然可以成功的运行程序并输出Hello,World.这是为什么呢?其实委托的绑定是个强类型引用被引用到的东西是不会被垃圾回收器销毁的除非我们用 evetObj.DoEvent - new MessageWriter(writerObj.WriteToConsole); 显示删除,这样带来的一个隐患是如若我们忘记删除那么就会造成内存泄漏。 怎样才能够在对象被销毁时跟随的事件也一起被废除呢 MS在WPF中引入了WeakEvent模式MSDN讲解了实现 WeakEvent 模式由三个方面组成 从 WeakEventManager 类派生一个管理器。在任何想要注册弱事件的侦听器的类上实现 IWeakEventListener 接口而不生成源的强引用。注册侦听器时对于想要侦听器使用该模式的事件不要使用该事件的常规的 add 和 remove 访问器请在该事件的专用 WeakEventManager 中改用“AddListener”和“RemoveListener”实现。 也就是每个要用到的事件要有一个从WeakEventManager 类派生的管理器而引用到的类要继承于IWeakEventListener接口并实现里面的方法注册侦听器时不要用之前的add,remove或,-来访问要用对应该事件的WeakEventManager管理器中的AddListener和RemoveListener方法实现. 下面让我们先来改写委托并实现从WeakEventManager派生的TextEventManager public class TextEventArgs : EventArgs { public TextEventArgs(string text) { Text text; } public string Text { get; set; } } public delegate void MessageWriter(object sender, TextEventArgs e); public class TextEventManager : WeakEventManager { private TextEventManager() { } public static void AddListener(EventClass source, IWeakEventListener listener) { //调用WeakEventManager的保护方法来注册 CurrentManager.ProtectedAddListener(source, listener); } private void OnTextPrint(object sender, TextEventArgs e) { //这句就是调用类的IWeakEventListener接口,如果类无法处理返回false会发出异常 base.DeliverEvent(sender, e); } public static void RemoveListener(EventClass source, IWeakEventListener listener) { //调用WeakEventManager的保护方法来注销 CurrentManager.ProtectedRemoveListener(source, listener); } protected override void StartListening(object source) { EventClass changed (EventClass)source; changed.DoEvent new MessageWriter(this.OnTextPrint); } protected override void StopListening(object source) { EventClass changed (EventClass)source; changed.DoEvent - new MessageWriter(this.OnTextPrint); } private static TextEventManager CurrentManager { get { Type managerType typeof(TextEventManager); //查看 TextEventManager 是否已被注册没有注册将返回null TextEventManager currentManager (TextEventManager)WeakEventManager. GetCurrentManager(managerType); if (currentManager null) { currentManager new TextEventManager(); //第一次调用的话把该Manager进行注册 WeakEventManager.SetCurrentManager(managerType, currentManager); } return currentManager; } } } 从其中的StartListening和StopListening方法中可以看出其实是当发现调用类被销毁时来显示把委托给删除的。 不知道你有没感觉到为了一个事件而要写这么个Manager似乎有些烦幸好EventClass和ConsoleWriter变化倒不是很大。 注意ReceiveWeakEvent实现了调用则返回 true,接收到的事件不是预期的事件则返回 false. public class EventClass { public event MessageWriter DoEvent; public void ConsoleOut(string text) { if (DoEvent ! null) DoEvent(this,new TextEventArgs(text)); } } public class ConsoleWriter : IWeakEventListener { private void WriteToConsole(string text) { Console.Write(text); } public bool ReceiveWeakEvent(Type managerType, object sender, EventArgs e) { if (managerType typeof(TextEventManager)) { WriteToConsole(((TextEventArgs)e).Text); } else { return false; } return true; } } // Client code... ConsoleWriter writerObj new ConsoleWriter(); EventClass evetObj new EventClass(); TextEventManager.AddListener(evetObj, writerObj); //TextEventManager.RemoveListener(evetObj, writerObj); writerObj null; //GC.Collect(); evetObj.ConsoleOut(Hello, World!); 当然你没有把GC.Collect()的注释去掉的话依旧有Hello,World被打印所以WeakEvent只在于垃圾回收之后才会把委托事件给自动删除。 最后弱弱的说一句如果你能够在销毁对象的时候加上-类似的动作应该就不需要劳累的写这么多了。 转载于:https://www.cnblogs.com/Curry/archive/2008/10/30/1322647.html