【问题标题】:Hooking into a Storyboard.Complete event with MVVM pattern使用 MVVM 模式连接 Storyboard.Complete 事件
【发布时间】:2013-02-08 07:15:33
【问题描述】:

基本上,我得到的是一个简单的警报消息设置。有一个单独警报的视图,其中一些数据绑定到 VM 的文本和颜色(绿色表示成功,红色表示错误等)。

<Border Margin="0 0 0 20" Background="{Binding Path=BackgroundColor}" BorderBrush="#D4D4D4" BorderThickness="1" CornerRadius="8">
    <Border.Effect>
        <DropShadowEffect Color="DarkGray"/>
    </Border.Effect>
<Grid >
        <Button Content="X" HorizontalAlignment="Left" 
            Margin="268,10,0,0" VerticalAlignment="Top" Width="20" RenderTransformOrigin="-0.48,0.727"/>
        <TextBlock x:Name="Message" 
           Foreground="White" FontWeight="SemiBold" FontSize="13px"
           Text="{Binding Path=Message}" 
           HorizontalAlignment="Left" Width="250"
           TextWrapping="Wrap"
           VerticalAlignment="Center" RenderTransformOrigin="-4.395,-0.038" Margin="7,13,0,25"/>
    </Grid>
</Border>

目标是在您单击“X”按钮时让它们消失,并带有漂亮的淡入淡出动画。我有一个故事板来处理褪色:

<UserControl.Triggers>
    <EventTrigger RoutedEvent="Button.Click">
        <BeginStoryboard>
            <Storyboard>
                <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)">
                    <EasingDoubleKeyFrame KeyTime="0" Value="1"/>
                    <EasingDoubleKeyFrame KeyTime="0:0:1" Value="0"/>
                </DoubleAnimationUsingKeyFrames>
            </Storyboard>
        </BeginStoryboard>
    </EventTrigger>
</UserControl.Triggers>

现在,我想做的是让 Caliburn.Micro 挂钩到情节提要的 Completed 事件,并使用一种方法来关闭实际视图(在另一个视图中有这些警报的可绑定集合,以便警报将堆叠然后根据用户的意愿被解雇)。

一开始我尝试过这样的事情,认为它与我的任何其他绑定一样工作:

<Storyboard cal:Message.Attach="[Event Completed] = [Action DismissMessage($source, $eventArgs)]">
<!-- rest of the codez... -->

但出现以下错误:

Cannot attach type "ActionMessage" to type "Storyboard". Instances of type "ActionMessage" can only be attached to objects of type "FrameworkElement".

我猜这很有意义......但是处理这种绑定的最佳方法是什么?我的思绪不可避免地开始徘徊在尝试一些 hacky 解决方案,例如从视图的代码隐藏中调用视图模型,但这似乎违反了 MVVM。

有什么建议吗?

【问题讨论】:

  • 基本上,CM 的动作实现适用于基于 FrameworkElement 的元素(有充分的理由)。这里有一个关于为什么什么不起作用(任何原因)的讨论:caliburnmicro.codeplex.com/discussions/250844 - 阅读一下,它可能会给你一些见解。至于解决方法,您可能会发现自己硬着头皮将一些代码放在视图代码隐藏中。一个想法可能是使用EventAggregator 并在情节提要完成时提供一个事件,然后在 VM 中订阅此事件以关闭对话框。

标签: wpf data-binding mvvm caliburn.micro


【解决方案1】:

我认为避免查看代码背后的最佳方法是使用 Blend 交互库中的 StoryboardCompletedTrigger

首先在您的 xaml 中包含正确的引用:

xmlns:i="http://schemas.microsoft.com/expression/2010/
xmlns:ie="http://schemas.microsoft.com/expression/2010/interactions"   

然后将您的交互触发器添加到您的视图中并将其操作设置为CallMethodAction

<i:Interaction.Triggers>
  <ie:StoryboardCompletedTrigger Storyboard="{StaticResource MyStoryboard}">
    <ie:CallMethodAction MethodName="DismissMessage" TargetObject="{Binding}"/>
  </ie:StoryboardCompletedTrigger>
</i:Interaction.Triggers>

【讨论】:

    【解决方案2】:

    正如我所评论的,聚合器可能是一种更简单的出路。

    StoryBoard 完成时,您可以通过聚合器(或特定的对话框聚合器)触发事件并在 VM 中处理该事件以关闭对话框

    例如如果您没有聚合器,您将需要提供聚合器的东西(不确定您是否使用 DI)

    public static DialogEventAggregatorProvider  
    {
        public static EventAggregator { get; set; } // Obviously instantiate this, I'll leave the code out for brevity
    }
    

    在你看来代码隐藏:

    public SomeView : UserControl
    {
        private void StoryBoard_Completed(object sender, SomeEventArgs e)
        {
            DialogEventAggregatorProvider.EventAggregator.Publish(new CloseDialogMessage()); // Add some args or what have you if it helps identify what dialog to close, but try not to break MVVM ;)
        }
    }
    

    在虚拟机中

    public SomeViewModel : Screen, IHandle<CloseDialogMessage>
    {
        public SomeViewModel() 
        {
            // Don't forget to subscribe or you'll be scratching your head
            DialogEventAggregatorProvider.EventAggregator.Subscribe(this);
        }
    
        public void Handle(CloseDialogMessage message) 
        { 
            // if(message.HasSomeValue) Here you could check the type of message etc.
               TryClose();
        }
    }
    

    它不是那么漂亮,但只要你保持通用的实现,你就可以在不违反 MVVM 原则的情况下逃脱

    【讨论】:

    • 我对 WPF 开发人员非常陌生,所以我不完全确定如何使用事件聚合器,但我认为它还不错,所以我会尝试一下。感谢您的详尽回复,我会报告!
    • 它是 Caliburn.Micro 的一部分,只是一个发布者/订阅者模型 - 您创建一个聚合器来调解订阅者和发布者之间的通信。订阅者需要调用Aggregator.Subscribe(subscriber),发布者使用Aggregator.Publish(message)。订阅者必须实现IHandle&lt;messageType&gt; 接口,其中messageType 是它将收听的消息类型(您可以传递任何您想要的消息类型)。消息处理程序将对当前正在侦听特定消息类型的所有订阅者触发。
    • 太棒了,这最终奏效了!就 messageType 的细节而言,我还有其他一些事情要解决,但事件处理效果很好。很高兴看到 CM 内置了功能来帮助解决这个问题。谢谢!
    • 很高兴它起作用了——通常有一种方法,这并不像能够处理任何类型的动作消息那样好,但它至少让 MVVM 的东西快乐并且一切都解耦跨度>
    【解决方案3】:

    您可以使用 Marlon Grech 附加命令行为库将该事件绑定到命令。

    库可以从这里下载: http://marlongrech.wordpress.com/2008/12/13/attachedcommandbehavior-v2-aka-acb/

    【讨论】:

    • 我试图查看它,但我没有看到它究竟有什么帮助......而且它来自 3 多年前,作者特别说它没有经过很好的测试,哈哈。不过感谢您的建议。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-03-30
    • 1970-01-01
    • 2023-03-28
    • 2021-06-11
    • 1970-01-01
    • 2020-05-30
    • 1970-01-01
    相关资源
    最近更新 更多