c# - 委托(delegate)、 Action 、事件、Lambda 表达式和 MVVM

标签 c# wpf events mvvm delegates

我花了几天时间试图理解 WPF 和 MVVM。它进行得非常缓慢,主要是因为我在事件和东西方面缺乏一些知识。下面我将尝试解释我对所有这些事情的理解:


方法 – 这个很简单,我认为不需要任何解释。任何程序的基本组成部分。

Delegate – 我认为它是指向方法的指针。我能想到的只有少数几个我想通过方法使用它的应用程序。

Action – 那个更棘手。我设法找到的信息说它是一个不返回值的委托(delegate)......那么它只是指向 void 方法的指针吗?我不明白这一点

事件 – 这个我一点都不懂。它正在与委托(delegate)一起解释,我不明白它是如何工作的以及它的用途。请注意,我正在使用事件编写 winforms 应用程序,但它只是从列表中选择所需的事件。

事件处理程序 – 更不清楚。

Lambda 表达式 – 也是使用 method 的另一种方式。我再次明白它不会返回任何东西,我可以在其中传递一些参数,但与 void 方法仍然没有太大区别。我见过一些应用程序,例如使用 LINQ,但我仍然不明白它是如何工作的。


首先我想说我了解 MVVM 的基本结构,什么是做什么等等。我遇到的问题是我不理解一些代码,它是如何工作的,因此我实际上不能自己写任何东西。我将使用一些教程作为示例,所以这里是:

S1:https://msdn.microsoft.com/en-us/magazine/dd419663.aspx#id0090030

S2:http://social.technet.microsoft.com/wiki/contents/articles/18199.event-handling-in-an-mvvm-wpf-application.aspx

我期望从你们那里得到一些指导或解释,我怎样才能接近和理解那些想法,至少让它们对我来说不那么可怕。在这里我将举一些例子,希望能告诉你我遇到了什么样的问题。


1) 第一个来自著名的 RelayCommand 类的 S1:

    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

我知道它应该做什么(名字不言而喻)。但是我不明白这东西是怎么工作的?它如何知道什么时候可以执行什么时候不能执行。那些添加和删除“命令”到底是什么?我试图阅读它,但没有帮助。

2) S1 的另一个示例:

    #region CloseCommand

    /// <summary>
    /// Returns the command that, when invoked, attempts
    /// to remove this workspace from the user interface.
    /// </summary>
    public ICommand CloseCommand
    {
        get
        {
            if (_closeCommand == null)
                _closeCommand = new RelayCommand(param => this.OnRequestClose());

            return _closeCommand;
        }
    }

    #endregion // CloseCommand

    #region RequestClose [event]

    /// <summary>
    /// Raised when this workspace should be removed from the UI.
    /// </summary>
    public event EventHandler RequestClose;

    void OnRequestClose()
    {
        EventHandler handler = this.RequestClose;
        if (handler != null)
            handler(this, EventArgs.Empty);
    }

    #endregion // RequestClose [event]

我再次知道它应该做什么,我什至明白这里基本上发生了什么,但我看不到这个“东西”实际上在哪里做某事。 OnRequestClose() 只是在创建处理程序,在我看来,它不会执行任何操作来关闭它应该关闭的内容。问题是,如果我什至看不到命令​​在哪里执行,我怎么能编写自己的命令。

3) 我认为这将是最后一个示例,这次来自 S2:

public ViewModel()
{
    _clickCommand = new DelegateCommand<string>(
        (s) => { /* perform some action */ }, //Execute
        (s) => { return !string.IsNullOrEmpty(_input); } //CanExecute
        );
}

这里的问题很简单。它使用 RelayCommand ctor 创建命令(或者至少是这个项目中的版本,这里称为“DelegateCommand”)。我不明白这些和 lambda 的使用。它有什么用?


当然这不是我遇到的所有问题,但我认为这会让任何愿意提供帮助的人知道我的问题是什么。我尽力解释我的问题,非常感谢任何帮助或指导。也许我对自己期望很高,但我觉得我需要了解所有这些东西才能写出严肃的东西。

无论如何,提前感谢大家的帮助。

最佳答案

您的宽泛问题的长答案。

让我先用一种简单的方法深入研究事件和事件处理程序

假设您所在的城市举办了一场盛大的事件,将接待您所在国家/地区的许多名人。

让我们为这个例子考虑三个客人
客人一是其他客人不认识的人
客人二是你们地区的名人,很少有人知道
嘉宾三在你们国家很有名

Consider the following assumptions

Nobody is waiting for guest one (0 EventHandler)

There are four people who are waiting for guest two (4 EventHandler's each person is an event handler waiting to greet)

There are three security personnel and 10 guests (out of which one person is also waiting for guest 2) waiting for guest 3 (13 EventHandler's)

场景一 当客人 1 到达 field 时(Event raised)没有任何反应

场景 2 当客人二到达会场(Event raised)时,四个人走向他/她并致以问候

场景 3 当客人三到达时(事件)你会看到安全人员提供掩护,十个人走向他/她并致以问候

Simple points to be observed
1. Event is just a notification that something has happened (like a delegate with a method signature)
2. EventHandler is an action as to what happens when a specific event has happened ( a method which implements the method signature defined by the delegate)
3. One event can have many eventhandlers (e.g.. scenario 2,3). Hence the syntax += in the below code sample

下面的代码将回答你的一些基本问题

class Program
{
    static void Main(string[] args)
    {
        var test = new Example();
        Console.ReadLine();
    }
}


class Example
{
    //This is the event definition
    delegate void ActionDelegate(string input1, string input2);

    // Two event handler which implements the signature of the event
    void ActionMethod(string a, string b)
    {
        Console.WriteLine(a + " " + b);
    }
    void ActionMethod2(string c, string d)
    {
        Console.WriteLine("Wow one more function called with parameter {0} and {1}", c, d);
    }

    delegate Tuple<string, string> LamdaDelegate(string input1, string input2);
    public Example()
    {
        //Did not declare any delegate member variable explicitly.
        //Clean and easy to understand
        Action<string, string> action = ActionMethod;

        // Had to define the delegate with method signature explicitly before using it
        ActionDelegate actionDelegate = ActionMethod; 
        actionDelegate += ActionMethod2; // Attaching more event handlers to the event

        //The below lambda expression implicitly means that it will take two inputs each of type string
        // and does not return anything. 
        //The type information is implicitly derived from the method signature of the delegate
        actionDelegate += (a, b) => Console.WriteLine("Called using lambda expression");


        //Below is a Lambda expression in which s and e each is of type string 
        //Since the return type of the delegate is Tuple<string,string> the same is returned by the expression
        //Lambda expression is using a delegate without defining a delegate explicitly. 
        LamdaDelegate myTuple = (s, e) => { return Tuple.Create(s, e); };

        //The above Lambda can be rewritten as
        myTuple += delegate (string a, string b) { return Tuple.Create(a, b); };

        //Invoking the event handlers. The event handlers are executed automatically when ever the event occurs 
        action("Hi", "called from action");
        actionDelegate("Hi", "called using explicitly defined delegate");
    }
}

我们为什么要使用委托(delegate)?
在上面的示例中,您看到 ActionMethod由两个不同的委托(delegate)使用(读取事件)。 如果没有委托(delegate),上面的例子将是肮脏的和不可读的。
上面的示例还显示了 Action这简化了明确定义委托(delegate)的需要。

现在进入命令
在 MVVM 中,传统事件被 Command 取代并且事件参数(读取委托(delegate)方法签名)被替换为 CommandParameter

在你的第三个问题中 DelegateCommand有一个类型参数是 string .所以你看到的 (s) 是一个变量,它存储从 UI 发送的输入字符串。现在由您决定是要使用输入 (s) 还是一起忽略它。在CanExecute部分你可以看到,即使输入 (s) 被传递,它也忽略了它并使用另一个成员变量 _input .

第二题一个事件RequestClose已定义,它在链接的图 7 中附加了一个事件处理程序。还要注意在同一图中 MenuItem绑定(bind)到 CloseCommandViewModel .
所以现在当你点击 MenuItem CloseCommand被调用,然后调用函数 OnRequestClose .此函数检查的第一件事是是否有人对事件 RequestClose 感兴趣(阅读收听)哪个MainWindow正在监听,因此调用 window.Close()

如需更多说明,请告诉我。

编辑

上面的代码示例仅适用于 Action<T>delegate .

通俗地说,让我这样说吧

如果我想根据一些外部 Action 做某事 我会使用事件。我将只定义一个事件并将其连接到 EventHandler。并等待 Action 发生。 (在上面的示例中,我不知道客人什么时候到达,但只要他们到达,EventHandler 就会自动响应)

另一方面,我将使用 delegate如果我想根据代码中定义的某些内部条件同时调用单个/多个函数。在上面的代码示例中,您可以看到我必须手动调用委托(delegate)

请忽略Tuple因为它只是一个没有实际重要性的返回值。

编辑 2

场景(_)(s) 类似。 两者都意味着您将获得一个参数作为输入,但在第一种情况下,开发人员试图说明在他们打算使用的 (s) 中不会使用或忽略此参数使用它

您不能只使用 (),因为这意味着 lambda 表达式不包含任何错误的输入参数,因为定义表明它将收到 string。作为输入。

关于c# - 委托(delegate)、 Action 、事件、Lambda 表达式和 MVVM,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31888792/

相关文章:

c# - 具有动态数据显示的 WPF 图表 : How can I show a regression line?

wpf - 何时使用自定义用户控件

c# - 如何在 MVVM 中管理多个窗口

Javascript 停止一个事件

c# - 如何使用闭包对象作为参数创建一个调用(或结合)另一个表达式的表达式?

c# - 类库找不到 MembershipUser

c# - 将节点添加到 TreeView 会导致 Thread-Exception

c# - 展开最后一个网格行以填充 wpf 应用程序中的窗口

javascript - 仅在第一次使用 javascript 时滚动页面时如何提醒?

javascript - 每次 div 改变其大小时如何运行一个函数?