65.9K
CodeProject 正在变化。 阅读更多。
Home

事件处理程序变得简单

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.87/5 (8投票s)

2016年3月11日

CPOL

7分钟阅读

viewsIcon

20090

本文将解释自定义事件处理程序工作所需的条件。

引言

首先,无需下载,只需简单地创建一个新的WPF(或Windows窗体)C#应用程序,复制代码,粘贴代码,运行代码。样式由您决定。必要时,我将指导您何时创建项目(例如类库、窗体等)。

在尝试解释C#中的事件处理程序时,我费了很大劲才找到一个真正简单的例子。即使找到一个看起来简单的例子,我也发现围绕代码的解释令人困惑。所以我想到为什么不为自己和他人创建一个呢?

背景

巩固知识的最佳方法是尝试教授它!

本项目只有一个XAML或Windows窗体(取决于您偏好的技术)和一个类库。通常,单个类库会为每个不同的类拆分成单独的文件,但这并非必需。为了尽可能保持简单,我已将所有事件类放在一个文件中。

我们将创建的自定义“事件”将包含两个string。一个将向界面返回自定义消息,另一个将提供工作状态。在这种情况下,我将从一个名为WorkStart的事件开始(虚构的名称!)。WorkStart将在我们的类库开始任何工作时触发。XAML(或Windows窗体)后面的代码将创建一个我们在类中定义的Worker对象。然后,我们将使用按钮点击来告诉该Worker对象StartWorkWorker类中的StartWork方法将引发事件,事件将在我们的窗体中处理,并且窗体textbox将更新。

开始编码

启动Visual Studio。我使用的是2013版,但2015版也可以使用。2012甚至2010等旧版本也应该可以。如果您没有Visual Studio,请前往Microsoft获取免费的社区版!(前提是您符合许可协议)。接下来,创建一个新的WPF(或Windows窗体)应用程序(使用C#)。您可以随意命名,但我称之为“EventSimplified”。项目初始化后,我们来创建一个新类。在解决方案资源管理器中选择您的项目名称并右键单击。选择“添加”,然后在底部选择“类”。(或者您也可以直接按Shift + Alt + C)。我将其命名为EventCode,但您可以随意命名。您将看到一个空的类库。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace EventSimplified
{
    class EventCode
    {
    }
}

好的,现在您的剪切和粘贴技能派上用场了!将下面的代码复制到剪贴板中。

// Delegate that handles the declaration in a class library as well as that in the User Interface
public delegate void WorkStartedEventHandler(object sender, WorkStartedEventArgs e);

//Class for specialized event handler
public class WorkStartedEventArgs : EventArgs
    {
        public string Status;
        public string Message;
        public WorkStartedEventArgs(string msg, string status)
        {
            Status = status;
            Message = msg;
        }
    }

//Class that will trigger the event (think of this as the event sender.  
//When something happens in here it triggers the event).
public class Worker
    {
        //Event declaration
	public  event WorkStartedEventHandler WorkStarted;
	
         //The class library method that triggers the event...
	 public void StartWork()
        {
                WorkStartedEventArgs WkStart = 
                new WorkStartedEventArgs("Awake!", "Starting Task!");
                OnWorkStart((object)this, WkStart);	
        }

        //The method that passes the event to the event handler
	void OnWorkStart(object o, WorkStartedEventArgs e)
        {
            if (WorkStarted != null)
                WorkStarted( o, e);
        }
    }

现在在您的类库中,高亮显示命名空间下面的类定义。确保您选中了两个花括号。

class EventCode
    {
    }

粘贴您复制的代码。看,是不是没那么糟糕!现在,在我们实际将XAML(Windows窗体不需要)和后台代码放入之前,让我们看看我们刚才做了什么。

类库中命名空间后的第一行现在包含以下内容

// Delegate that handles the declaration in a class library as well as that in the User Interface
public delegate void WorkStartedEventHandler(object sender, WorkStartedEventArgs e);

我们在这里声明了一个delegatedelegate本质上是一个方法模板,当您的事件触发时会调用它。delegate的重要部分是参数。参数定义了事件处理程序的签名,换句话说,就是必须传递给事件处理程序的内容。

下一部分定义了我们的WorkStartedEventArgs

//Class for specialized event handler
public class WorkStartedEventArgs : EventArgs
    {
        public string Status;
        public string Message;
        public WorkStartedEventArgs(string msg, string status)
        {
            Status = status;
            Message = msg;
        }
    }

这段代码创建了一个公共类。它继承自默认的EventArgs并添加了两个string。当WorkStartedEventArgs被创建时,它会传递两个string(我们上面讨论的消息和状态)。通过传入这两个项,我们可以设置我们WorkStartedEventArgs类的公共StatusMessage属性的值。

我们粘贴的下一节是我们的Worker类。它是将为我们执行工作的类库。

//Class that will trigger the event (think of this as the event sender.  
//When something happens in here it triggers the event).
public class Worker
    {
        //Event declaration
	public  event WorkStartedEventHandler WorkStarted;
	
         //The class library method that triggers the event...
	 public void StartWork()
        {
                WorkStartedEventArgs WkStart = 
                new WorkStartedEventArgs("Awake!", "Starting Task!");
                OnWorkStart((object)this, WkStart);	
        }

        //The method that passes the event to the event handler
	void OnWorkStart(object o, WorkStartedEventArgs e)
        {
            if (WorkStarted != null)
                WorkStarted( o, e);
        }
    }

首先,我们创建WorkStartedEventHandler并将其命名为WorkStarted。我们将在UI中再次看到它,并将其绑定到窗体级别的方法。类的public void WorkStart部分是我们将在XAML窗体的代码中调用以开始工作的方法。StartWork方法创建一个新的WorkStartedEventArgs对象并传递两个string,如我们上面所定义。然后,我们调用类级别的OnWorkStart方法,传入被转换为对象[(object) this]的类对象,然后是创建的WorkStartedEventArgs对象。我们确保WorkStarted事件处理程序不为null,然后调用事件处理程序,传入类对象oWorkStartedEventArgs对象e

现在我们已经完成了所有类库,接下来让我们开始用户界面及其后面的代码。

XAML (WPF) 用户请到这里

<Window x:Class="EventSimplified.MainWindow" 
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
</Grid>
</Window>

又到了剪切和粘贴的时候了...这会创建一个非常难看的表单。我提前道歉,但它功能齐全,并且可以轻松地查看结果。复制以下代码

<Window x:Class="EventSimplified.MainWindow"         
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"         
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"        
Title="MainWindow" Height="350" Width="525">    
<StackPanel>        
<TextBox Name="Status" Margin="10,10,0,0" />        
<TextBox Name="Message" Margin="10,10,0,0" />        
<Button Content="Button" Name="btnStart" 
Click="btnStart_Click" />     
</StackPanel>
</Window>

选择您的整个XAML窗口,然后粘贴您上面复制的代码。我很抱歉,它确实很丑!请注意,如果您的应用程序名称与EventSimplified不同,则需要在顶部行更改此内容,并将您的应用程序名称替换为EventSimplified

打开XAML页面后面的代码(当光标在XAML中时按F7)。同样,您可以剪切并粘贴此代码(假设您没有更改MainWindow的名称!)将其粘贴在命名空间的开括号之后。

    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        //new instance of the worker class.
        Worker wk1 = new Worker();
        public MainWindow()
        {
            InitializeComponent();
            //Create the event handler for the form and assign it to wk1_started
            wk1.WorkStarted += new WorkStartedEventHandler(wk1_WorkStarted);
        }
        //Event Handler in the UI - This is what you want the UI to do When the event is triggered.

        void wk1_WorkStarted(object sender, WorkStartedEventArgs e)
        {
            //OK on the Form I have two Textboxes One Named Status, the Other Named Message
            Status.Text = e.Status;
            Message.Text = e.Message;
        }

        private void btnStart_Click(object sender, RoutedEventArgs e)
        {
            //Toggle the StartWork method in the class that will create the event args 
            //and call the OnWorkStart method to toggle the event handler
            wk1.StartWork();
        }
    }

Windows 窗体用户请到这里

对于使用Windows Forms的用户,您现在需要打开默认窗体。

向其中添加两个文本框。将其中一个命名为Status,另一个命名为Message。在上面放置一个按钮并将其命名为btnStart。按F7查看代码。使用您的剪切和粘贴技能使窗体看起来像下面的窗体。

public partial class Form1 : Form
    {
        //new instance of the worker class.
        Worker wk1 = new Worker();
        public Form1()
        {
            InitializeComponent();
            wk1.WorkStarted += new WorkStartedEventHandler(wk1_WorkStarted);
        }

        void wk1_WorkStarted(object sender, WorkStartedEventArgs e)
        {
            //OK on the Form I have two Textboxes One Named Status, the Other Named Message
            Status.Text = e.Status;
            Message.Text = e.Message;
        }
        
        private void btnStart_Click(object sender, EventArgs e)
        {
            //Toggle the StartWork method in the class that will create the event args 
            //and call the OnWorkStart method to toggle the event handler
            wk1.StartWork();
        }

WPF 和 Windows Forms 用户联合起来阅读下面的内容!

在我们运行它之前,让我们看看我们做了什么。我们添加的第一行是创建一个新的Worker对象。我们在类库中定义了它。这就是这一行

//new instance of the worker class.
        Worker wk1 = new Worker();

然后,在窗体初始化代码之后(在XAML和Windows窗体上都一样!),我们将窗体的wk1_WorkStarted方法绑定到类中的WorkStarted事件。这是允许类在窗体上引发事件的管道,并告诉窗体在事件引发时调用什么(如下所示)。请注意,参数与我们在类库中声明的委托匹配。它必须匹配,否则将无法工作!

  void wk1_WorkStarted(object sender, WorkStartedEventArgs e)
        {
            //OK on the Form I have two Textboxes One Named Status, the Other Named Message
            Status.Text = e.Status;
            Message.Text = e.Message;
        }

这是您为响应事件而创建的代码。您可以看到这有多强大。让想象力自由驰骋一会儿...好了,回到正题,让我们完成代码的讨论。最后,在按钮点击事件下,我们放置了启动整个过程的代码。当您运行代码时,窗体会创建Worker类对象(wk1),但什么也没有发生。那是因为您需要点击按钮才能开始工作!

 //Toggle the StartWork method in the class that will create the event args 
 //and call the OnWorkStart method to toggle the event handler
 wk1.StartWork();

好的,花一分钟运行您的代码。成功了吗?如果不是,检查错误,更正并重试。

现在我们有了一个简单的例子,在下一节中,我将扩展它以展示一个多线程的例子。为什么要多线程?假设您在类中运行的工作需要一段时间(2-3分钟甚至更长)。您希望用户界面保持响应,向用户更新进度,而不是只变白并只显示一个等待光标。用户可能会认为您的应用程序已锁定并将其终止。应用程序并未锁定,但运行用户界面的线程与执行工作的线程是同一个,因此它很忙,无法更新界面。多线程允许您在一个线程上运行用户界面,而其他进程可以在其他线程上运行,从而为您提供一个响应迅速的界面,向用户更新长时间运行进程的状态。

历史

  • 2016年3月11日 提交此第一个版本!
© . All rights reserved.