【问题标题】:Run two Commands and Animation on Button Click在按钮单击时运行两个命令和动画
【发布时间】:2015-01-11 06:59:59
【问题描述】:

在我的应用程序中,我有一个 Left Grid 和一个 Right Grid。最初,Left Grid 完全展开 (GridLength = 7*),而 Right Grid 不可见 (Width = 0*)。

如果单击LeftGrid 中的Button 并且Right Grid 未展开,则Right Grid 应展开为Width3*

如果Right Grid 被展开并且LeftGrid 中的Button 被连续点击两次,那么RightGrid 应该收缩回0*Width

这些扩展/收缩应该是Animated

当点击Button 时,必须发生三件事。

1) 所选Buttonid 应存储在ViewModel 中的Property 中。

2) 将设置RightGridWidth 存储在Property 中的ViewModel 中。这个Command 负责处理两个连续的点击情况。

3) 最后,Animation 应该运行。

我遇到了一些问题:

1) 如何将两个Commands 绑定到一个Button

<Button DockPanel.Dock="Top"                        
   Command="{Binding DataContext.SetSelectedItemCommand, 
             RelativeSource={RelativeSource 
             AncestorType={x:Type ItemsControl}}}"
             CommandParameter="{Binding id}"
             Command="{Binding ElementName=root, Path=DataContext.ChangeRightGridWidthCommand}"          
>

这是不允许的。

我一直在看这篇文章 (http://www.codeproject.com/Articles/25808/Aggregating-WPF-Commands-with-CommandGroup)。

但是,我不确定这是否对我有用,因为我在 ViewModel 中定义了 Commands

2) 我正在使用自定义 GridLength 动画类,因为我需要将 * 用于我的 Widths

public class GridLengthAnimation : AnimationTimeline
{

    public static readonly DependencyProperty ToProperty =
    DependencyProperty.Register(
        "To", typeof(GridLength), typeof(GridLengthAnimation));

    public GridLength To
    {
        get { return (GridLength)GetValue(ToProperty); }
        set { SetValue(ToProperty, value); }
    }

    public override Type TargetPropertyType
    {
        get { return typeof(GridLength); }
    }

    protected override Freezable CreateInstanceCore()
    {
        return new GridLengthAnimation();
    }

    public override object GetCurrentValue(
        object defaultOriginValue, object defaultDestinationValue,
        AnimationClock animationClock)
    {
         return new GridLength(To.Value,
            GridUnitType.Star);
    }
}

我打算在我的XAML 中使用这样的东西:

<proj:GridLengthAnimation
          Storyboard.TargetName="rightGrid"
          Storyboard.TargetProperty="Width"
          To="RightGridWidth" Duration="0:0:2" />

<Grid.ColumnDefinitions>
      <ColumnDefinition Name="leftGrid"  Width="7*"/>
      <ColumnDefinition Name="rightGrid" Width="{Binding RightGridWidth}"/>
 </Grid.ColumnDefinitions>

我应该把上面的XAML代码放在:

  <EventTrigger RoutedEvent="Button.Click">

如果是这样,我的CommandsEventTrigger 会先运行吗?我需要先运行我的Commands,因为只有在Storyboard 运行时,To 的值才会正确设置。

我很困惑如何将所有这些放在一起。

任何帮助/建议将不胜感激!

【问题讨论】:

    标签: c# wpf animation grid


    【解决方案1】:

    快速而肮脏地使用您已有的东西,即运行动画的 EventTrigger。

    RightGridWidth 属性(带有更改通知)添加到您的视图模型:

    public GridLength RightGridWidth
    {
        get { return rightGridWidth; }
        set
        {
            rightGridWidth = value;
            OnPropertyChanged("RightGridWidth");
        }
    }
    

    SetSelectedItemCommand 的执行处理程序中切换属性值:

    if (RightGridWidth.Value == 0d)
    {
        RightGridWidth = new GridLength(3d, GridUnitType.Star);
    }
    else
    {
        RightGridWidth = new GridLength(0d, GridUnitType.Star);
    }
    

    ClickEventTrigger中运行动画:

    <Button Content="Click" Command="{Binding SetSelectedItemCommand}">
        <Button.Triggers>
            <EventTrigger RoutedEvent="Button.Click">
                <BeginStoryboard>
                    <Storyboard>
                        <local:GridLengthAnimation
                            Storyboard.Target="{Binding ElementName=col2}"
                            Storyboard.TargetProperty="Width"
                            Duration="0:0:1" To="{Binding RightGridWidth}"/>
                    </Storyboard>
                </BeginStoryboard>
            </EventTrigger>
        </Button.Triggers>
    </Button>
    

    最后,使用动画的GetCurrentValue 的工作实现,它必须返回一个取决于CurrentProgress 的值:

    public override object GetCurrentValue(
        object defaultOriginValue, object defaultDestinationValue,
        AnimationClock animationClock)
    {
        var from = (GridLength)defaultOriginValue;
    
        if (from.GridUnitType != To.GridUnitType ||
            !animationClock.CurrentProgress.HasValue)
        {
            return from;
        }
    
        var p = animationClock.CurrentProgress.Value;
    
        return new GridLength(
            (1d - p) * from.Value + p * To.Value,
            from.GridUnitType);
    }
    

    一个完全不同的也许更好的方法是使用视觉状态。见VisualStateManager

    【讨论】:

    • 谢谢克莱门斯。我面临的一个小问题是,当我最初使用SetSelectedItemCommand 更改RightGridWidth 时,GetCurrentValue() 中的from 已经设置为3,但To 的值仍在0 上。然而,在最初的click 之后,它似乎运行良好。
    • 无论如何这都不是一个完美的解决方案,因为它将行为拆分为在命令处理程序中切换动画目标值并在 EventTrigger 中启动动画。我的建议仍然是在代码后面的按钮单击处理程序中完成所有这些操作。
    • 我不知道你提到的VisualStateManager的存在。您认为我可以使用它并且仍然能够实现我在​​问题中提到的逻辑吗?
    • 我想是的。你可能只是试一试。声明两个不同的视觉状态,右侧网格有两个不同的宽度,并在它们之间切换。
    • 好的。谢谢克莱门斯
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-04-10
    • 1970-01-01
    • 1970-01-01
    • 2013-01-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多