代理,操作,事件,Lambda表达式和MVVM

Bie*_*lik 5 c# wpf events delegates mvvm

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


方法 - 这个很简单,我认为不需要任何解释.任何计划的基本要素.

委托 - 我看到的方式是方法指针.我只想到几个应用程序,我想在方法上使用它.

行动 - 那个甚至更棘手.我设法找到的信息说它是一个不返回值的委托......所以它只是指向void方法的指针吗?我没有看到这一点

事件 - 这个我根本没有得到.这是与代表解释,我不明白它是如何工作的,它是什么.注意我正在使用写winforms应用程序的事件,但它只是从列表中选择所需的事件.

事件处理程序 - 更加不清楚.

Lambda表达式 - 也是使用方法的另一种方式.我再次理解它不会返回任何内容,我可以在其中传递一些参数,但仍然与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; }
    }
Run Code Online (Sandbox Code Playgroud)

我知道它应该做什么(名字不言自明).但我不明白这件事是如何运作的?它是如何知道何时制作可执行文件的,何时不是.那些添加和删除"命令"的是什么?我试着阅读它,但它没有帮助.

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]
Run Code Online (Sandbox Code Playgroud)

我再次知道它应该做什么,我甚至理解这里基本上发生了什么,但我没有看到这个"事物"实际上在做什么.OnRequestClose()只是创建了一个处理程序,在我看来并没有做任何事情来关闭它想要关闭的东西.问题是,如果我甚至没有看到命令执行的位置,我怎么能编写自己的命令.

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

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

这里的问题非常简单.它使用RelayCommand ctor创建命令(或至少它在此项目中的版本,此处称为"DelegateCommand").我不明白那些和lambda的使用.它是为了什么?


当然,这不是我遇到的所有问题,但我认为这会让任何愿意帮助的人知道我的问题.我试图尽可能地解释我的问题,我真的很感激任何帮助或指导.也许我对自己的期望很高,但我觉得我需要知道所有这些东西才能写出任何严肃的东西.

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

San*_*esh 2

针对您的广泛问题的长答案。

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

假设您所在的城市举办了一场大型活动,将接待来自您所在国家的许多名人。

在此示例中,我们考虑三位客人 客人
一是其他客人不认识的人
客人二是您所在地区的名人,很少有人知道
客人三在您的国家/地区非常有名

考虑以下假设

没有人在等待客人一 (0 EventHandler)

有四个人正在等待客人二(4个EventHandler的每个人都是一个等待迎接的事件处理程序)

有 3 名保安人员和 10 名客人(其中 1 人也在等待客人 2)等待客人 3(13 个 EventHandler)

场景 1 当客人 1 到达场地(引发事件)时,没有任何反应

场景 2 当嘉宾二到达会场(事件引发)时,四人走向他/她并致以问候

场景 3 当客人三到达(活动)时,您将看到安全部队提供掩护,十个人走向他/她并致以问候

需要观察的简单要点
1. 事件只是发生某事的通知(例如具有方法签名的委托)
2. EventHandler 是关于当特定事件发生时发生的情况的操作(实现定义的方法签名的方法)由委托)
3. 一个事件可以有多个事件处理程序(例如场景 2,3)。因此,下面的代码示例中的语法 +=

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

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");
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

第二个问题RequestClose定义了一个事件,该事件附加了链接图 7 中的事件处理程序。另请注意,在同一图中,MenuItem绑定到CloseCommand中的ViewModel
现在,当您单击 时,就会调用该MenuItem函数CloseCommand,然后它会调用该函数OnRequestClose。该函数检查的第一件事是是否有人对正在侦听的事件感兴趣(读取侦听)RequestCloseMainWindow因此调用window.Close()

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

编辑

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

用外行的话来说,让我这样说

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

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

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

编辑2

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

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