前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C# WPF MVVM开发框架Caliburn.Micro关于关于Actions⑤

C# WPF MVVM开发框架Caliburn.Micro关于关于Actions⑤

作者头像
用户9127601
发布2022-01-13 08:37:13
2K0
发布2022-01-13 08:37:13
举报
文章被收录于专栏:dotNET编程大全dotNET编程大全

01

关于Actions

为了开始我们的研究,我们将以简单的“Hello”示例为例,看看当我们显式地创建操作而不是使用约定时,它是什么样子。以下是Xaml:

<UserControl x:Class="Caliburn.Micro.Hello.ShellView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
             xmlns:cal="http://www.caliburnproject.org">
    <StackPanel>
        <Label Content="Hello please write your name" />
        <TextBox x:Name="Name" />
        <Button Content="Click Me">
            <i:Interaction.Triggers>
                <i:EventTrigger EventName="Click">
                    <cal:ActionMessage MethodName="SayHello" />
                </i:EventTrigger>
            </i:Interaction.Triggers>
        </Button>
    </StackPanel>
</UserControl> 

如您所见,Actions功能利用Microsoft.Xaml.Behaviors作为其触发机制。这意味着您可以使用从Microsoft.Xaml.Behaviors.TriggerBase继承的任何内容来触发ActionMessage的发送。

①也许最常见的触发器是EventTrigger,但是您可以创建几乎任何可以想象的触发器,或者利用社区已经创建的一些常见触发器。当然,ActionMessage是这个标记中特定于Caliburn.Micro的部分。它表示当触发发生时,我们应该发送一条“SayHello”的消息。那么,为什么我在描述此功能时使用“send a message”而不是“execute a method”?这是有趣而有力的部分。ActionMessage在可视树中冒泡搜索可以处理它的目标实例。如果找到一个目标,但没有“SayHello”方法,框架将继续冒泡,直到找到一个,如果没有找到“handler”,则抛出异常。

②ActionMessage的这种冒泡特性在许多有趣的场景中都很有用,主/细节是一个关键用例。另一个需要注意的重要特征是动作卫士。当为“SayHello”消息找到处理程序时,它将检查该类是否也有一个名为“CanSayHello”的属性或方法。如果您有一个guard属性,并且您的类实现了INotifyPropertyChanged,那么框架将观察该属性中的更改,并相应地重新评估该保护。我们将在下面更详细地讨论方法保护。

02

Action 目的

现在您可能想知道如何指定ActionMessage的目标。看看上面的标记,没有任何可见的迹象表明目标是什么。那么,这是从哪里来的呢?由于我们使用了模型优先的方法,当Caliburn.Micro(以下简称CM)创建视图并使用ViewModelBinder将其绑定到ViewModel时,它为我们设置了此方法。通过ViewModelBinder的任何操作都将自动设置其操作目标。但是,您也可以使用附加的属性Action.Target自行设置。设置此属性会将ActionMessage“handler”放置在与您声明属性的节点相连的可视树中。它还将DataContext设置为相同的值,因为您通常希望这两个值相同。但是,如果愿意,可以从DataContext中更改Action.Target。只需使用Action.TargetWithoutContext附加属性即可。Action.Target的一个优点是可以将其设置为System.String,CM将使用该字符串从IoC容器中解析实例,并使用提供的值作为其键。如果您愿意的话,这将为您提供一种很好的方式来执行视图优先MVVM。如果需要设置Action.Target,并且还需要应用Action/Binding约定,则可以以相同的方式使用Bind.Model attached属性。

先看

//跳过这一节。

让我们看看如何使用视图优先技术(gasp!)将此应用于实现MVVM。下面是我们如何更改引导程序:

public class MefBootstrapper : BootstrapperBase
{
    //same as before

    protected override void OnStartup(object sender, StartupEventArgs e)
    {
        Application.RootVisual = new ShellView();
    }

    //same as before
}

因为我们先使用视图,所以我们继承了非通用引导程序。MEF配置与前面看到的相同,因此为了简洁起见,我省略了它。唯一改变的是视图的创建方式。在这个场景中,我们只需覆盖OnStartup,自己实例化视图并将其设置为RootVisual(在WPF的情况下为callshow)。接下来,我们将通过添加一个显式命名的契约,稍微改变导出ShellViewModel的方式:

[Export("Shell", typeof(IShell))]
public class ShellViewModel : PropertyChangedBase, IShell
{
    //same as before
}

最后,我们将更改视图以拉入VM并执行所有绑定:

<UserControl x:Class="Caliburn.Micro.ViewFirst.ShellView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:cal="http://www.caliburnproject.org"
             cal:Bind.Model="Shell">
    <StackPanel>
        <TextBox x:Name="Name" />
        <Button x:Name="SayHello"
                Content="Click Me" />
    </StackPanel>
</UserControl>

请注意Bind.Model attached属性的使用。这将通过IoC容器中的键解析VM,设置Action.Target和DataContext,并应用所有约定。我认为展示如何使用CM完全支持View First development会很好,但我主要想说明可以通过哪些方式为操作设定目标,以及使用每种技术的含义。以下是可用附加属性的摘要:

Action.Target

将Action.Target属性和DataContext属性都设置为指定实例。字符串值用于从IoC容器解析实例。

Action.TargetWithoutContext

仅将Action.Target属性设置为指定实例。字符串值用于从IoC容器解析实例。

Bind.Model

首先查看-将Action.Target和DataContext属性设置为指定实例。将约定应用于视图。字符串值用于从IoC容器解析实例。(在根节点上使用,如Window/UserControl/Page。)

Bind.ModelWithoutContext

首先查看-将Action.Target设置为指定实例。将约定应用于视图。(在DataTemplate内部使用。)

View.Model

ViewModel First–定位指定VM实例的视图并将其注入内容站点。将VM设置为Action.Target和DataContext。将约定应用于视图。

Action Parameters

现在,让我们看一下ActionMessage另一个有趣的方面:参数。要看到这一点,让我们切换回原始的ViewModel First bootstrapper等,首先将ShellViewModel更改为如下所示:

using System.Windows;

public class ShellViewModel : IShell
{
    public bool CanSayHello(string name)
    {
        return !string.IsNullOrWhiteSpace(name);
    }

    public void SayHello(string name)
    {
        MessageBox.Show(string.Format("Hello {0}!", name));
    }
}

这里有几件事需要注意。首先,我们现在使用的是一个完全的POCO类;这里没有INPC的问题。其次,我们在SayHello方法中添加了一个输入参数。最后,我们将CanSayHello属性更改为一个方法,该方法具有与操作相同的输入,但具有bool返回类型。现在,让我们看看Xaml:

<UserControl x:Class="Caliburn.Micro.HelloParameters.ShellView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
             xmlns:cal="http://www.caliburnproject.org">
    <StackPanel>
        <TextBox x:Name="Name" />
        <Button Content="Click Me">
            <i:Interaction.Triggers>
                <i:EventTrigger EventName="Click">
                    <cal:ActionMessage MethodName="SayHello">
                        <cal:Parameter Value="{Binding ElementName=Name, Path=Text}" />
                    </cal:ActionMessage>
                </i:EventTrigger>
            </i:Interaction.Triggers>
        </Button>
    </StackPanel>
</UserControl>

我们的标记现在有一个修改:我们使用ElementName绑定将参数声明为ActionMessage的一部分。您可以有任意数量的参数。值是DependencyProperty,因此所有标准绑定功能都应用于参数。我有没有说过你可以混合做这些?

这有一点很好,每次参数值更改时,我们都会调用与操作关联的guard方法(在本例中为CanSayHello),并使用其结果更新ActionMessage附加到的UI。继续运行应用程序。您将看到它的行为与前面的示例中相同。

除了文字值和绑定表达式外,还有许多有用的“特殊”值可用于参数。通过这些功能,您可以方便地访问常见的上下文信息:

$eventArgs

将EventArgs或输入参数传递给操作。注意:对于保护方法,这将是null,因为触发器实际上没有发生。

$dataContext

传递ActionMessage附加到的元素的DataContext。这在主/详细场景中非常有用,在主/详细场景中,ActionMessage可能会冒泡到父VM,但需要携带要执行操作的子实例。

$source

触发要发送的ActionMessage的实际框架元素。

$view

绑定到ViewModel的视图(通常是用户控件或窗口)。

$executionContext

操作的执行上下文,其中包含上述所有信息及更多信息。这在高级场景中很有用。

$this

操作附加到的实际UI元素。在这种情况下,元素本身不会作为参数传递,而是作为其默认属性传递。

必须以“$”开头变量,但CM对该名称的处理不区分大小写。可以通过向MessageBinder.SpecialValue添加值来扩展这些功能。

注意:使用特殊值,如$this或命名元素

如果不指定属性,CM将使用默认属性,该属性由特定控件约定指定。对于button,该属性恰好是“DataContext”,而TextBox默认为Text,SelectedItem的选择器等。在视图中使用对另一个命名控件的引用而不是$this时,也会发生同样的情况。以下内容:使CM将名为“someTextBox”的文本框中包含的文本传递给MyAction。实际控制从未传递给操作的原因是VM不应该直接处理UI元素,因此约定不鼓励这样做。但是,请注意,无论如何都可以使用扩展语法(基于System.Windows.Interactivity)填充参数或自定义解析器轻松访问控件本身。

枚举值

如果要将枚举值作为参数传递,则需要将该值作为(大写)字符串传递:

...
<Fluent:Button Header="Go!" cal:Message.Attach="[Event Click] = [Action MethodWithEnum('MONKEY')]" />
public enum Animals
{
  Unicorn,
  Monkey,
  Dog
}

public class MyViewModel
{ 
    public void MethodWithEnum(Animals a)
    {
         Animals myAnimal = a;
    }
}

Xamarin Forms

对于Xamarin表单,只有$this参数起作用,这是因为在Xamarin表单中遍历可视化树有点不同。

给智者的话

参数是一个方便的特性。它们非常强大,可以帮助你摆脱一些棘手的问题,但它们很容易被滥用。就我个人而言,我只在最简单的场景中使用参数。他们为我工作得很好的一个地方是登录表单。如前所述,另一个场景是主/细节操作。

现在,你想看看真正邪恶的东西吗?将Xaml更改回以下状态:

<UserControl x:Class="Caliburn.Micro.HelloParameters.ShellView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <StackPanel>
        <TextBox x:Name="Name" />
        <Button x:Name="SayHello" 
                Content="Click Me" />
    </StackPanel>
</UserControl>

运行应用程序将为您确认CM的约定甚至可以理解ActionMessage参数。我们将在将来更多地讨论约定,但是您应该很高兴知道这些约定不区分大小写,甚至可以检测前面提到的“特殊”值。

03

Action Bubbling

现在,让我们来看一个简单的主/细节场景,该场景演示了ActionMessage冒泡,但让我们使用一种简化语法来实现它,该语法设计得更加方便开发人员。我们将首先添加一个名为Model的简单新类:

using System;

public class Model
{
    public Guid Id { get; set; }
}

然后我们将ShellViewModel更改为:

using System;
using System.ComponentModel.Composition;

[Export(typeof(IShell))]
public class ShellViewModel : IShell
{
    public BindableCollection<Model> Items { get; private set; }

    public ShellViewModel()
    {
        Items = new BindableCollection<Model>{
            new Model { Id = Guid.NewGuid() },
            new Model { Id = Guid.NewGuid() },
            new Model { Id = Guid.NewGuid() },
            new Model { Id = Guid.NewGuid() }
        };
    }

    public void Add()
    {
        Items.Add(new Model { Id = Guid.NewGuid() });
    }

    public void Remove(Model child)
    {
        Items.Remove(child);
    }
}

现在,我们的shell有了一个模型实例集合,并且能够在集合中添加或删除。请注意,Remove方法只接受一个Model类型的参数。现在,让我们更新ShellView:

<UserControl x:Class="Caliburn.Micro.BubblingAction.ShellView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:cal="http://www.caliburnproject.org">
    <StackPanel>
        <ItemsControl x:Name="Items">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal">
                        <Button Content="Remove"
                                cal:Message.Attach="Remove($dataContext)" />
                        <TextBlock Text="{Binding Id}" />
                    </StackPanel>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
        <Button Content="Add"
                cal:Message.Attach="Add" />
    </StackPanel>
</UserControl>

Message.Attach

首先要注意的是,我们正在使用更为Xaml开发人员友好的机制来声明ActionMessages。Message.Attach属性由一个简单的解析器支持,该解析器接受其文本输入并将其转换为您之前看到的完整Interaction.Trigger/ActionMessage。如果您主要在Xaml编辑器中工作,而不是在设计器中工作,那么您会喜欢Message.Attach。请注意,Message.Attach声明都没有指定应该发送消息的事件。如果不使用该事件,解析器将使用ConventionManager来确定用于触发器的默认事件。对于按钮,它是单击。你总是可以直截了当地说粗话。下面是如果我们声明所有内容,删除消息的完整语法的样子:

<Button Content="Remove" cal:Message.Attach="[Event Click] = [Action Remove($dataContext)]" />

假设我们要用Message.Attach语法重新编写参数化的SayHello操作。它看起来是这样的:

<Button Content="Click Me" cal:Message.Attach="[Event Click] = [Action SayHello(Name.Text)]" />

但我们也可以利用解析器的一些智能默认值,如下所示:

<Button Content="Click Me" cal:Message.Attach="SayHello(Name)" />

您还可以将文字指定为参数,甚至可以通过用分号分隔来声明多个操作:

<Button Content="Let's Talk" cal:Message.Attach="[Event MouseEnter] = [Action Talk('Hello', Name.Text)]; [Event MouseLeave] = [Action Talk('Goodbye', Name.Text)]" />

警告

那些要求我将此功能扩展为一个完整的表达式解析器的开发人员将被带回来……处理。附加并不是将代码塞进Xaml。它的目的是提供一种简化的语法,用于声明何时/向ViewModel发送哪些消息。请不要滥用这个。

如果还没有,请运行该应用程序。当您看到消息bubbling如广告中所宣传的那样工作时,您的任何疑问都有望得到解决:)我想指出的另一点是,CM会自动对参数执行类型转换。例如,您可以将TextBox.Text输入到System.Double参数中,而不必担心铸造问题。

因此,我们已经讨论了如何将Interaction.Triggers与ActionMessage结合使用,包括参数与文字、元素绑定3和特殊值的使用。我们已经讨论了根据您的需要/体系结构样式设置操作目标的各种方法:action.target、action.TargetWithoutContext、Bind.Model或View.Model。我们还看到了ActionMessage冒泡特性的一个示例,并使用streamlined Message.Attach语法对其进行了演示。一直以来,我们也看到了各种公约在起作用的例子。现在,我们还没有讨论ActionMessage的最后一个杀手级功能…协同程序。但是,这将不得不等到下次。

04

最后

原文标题:Caliburn.Micro Xaml made easy

原文链接:https://caliburnmicro.com/documentation/actions

翻译:dotnet编程大全

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-12-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 dotNET编程大全 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 01
    • 关于Actions
    • 02
      • Action 目的
        • Action Parameters
        • 03
          • Action Bubbling
            • Message.Attach
            • 04
              • 最后
              相关产品与服务
              容器服务
              腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档