WPF从MVVM中的代码隐藏启动动画

时间:2014-03-05 16:12:44

标签: c# wpf mvvm binding wpf-animation

我正在寻找代码隐藏在ControlTemplate中包含的控件上启动动画的方法。 在我的应用程序中,我有一个由ObservableCollection

创建的自定义模板按钮(播放菜单角色)

MainMenuViewModel:

/// <summary>
/// Menu items list
/// </summary>
private ObservableCollection<MenuItem> _items;

....

/// <summary>
/// Menu items list property
/// </summary>
public ObservableCollection<MenuItem> Items
{
    get { return _items; }
    set { _items = value; }
}

MainMenuView:

<UserControl x:Class="OfficeTourismeBrantome.Views.MainMenuView"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         mc:Ignorable="d" 
         d:DesignHeight="800" d:DesignWidth="300">
    <UserControl.Resources>
        <Style x:Key="MenuItemButtonStyle" TargetType="Button">
            <Setter Property="FontSize" Value="60" />
            <Setter Property="FontFamily" Value="Segoe" />
            <Setter Property="FontWeight" Value="UltraLight" />
            <Setter Property="Foreground" Value="#FFEBEDEA" />
            <!--<Setter Property="Height" Value="{Binding MenuLayout.MenuItemSize.Height}" />-->
            <Setter Property="HorizontalContentAlignment" Value="Right" />
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type Button}">
                        <ControlTemplate.Triggers>
                            <EventTrigger RoutedEvent="Button.Click">
                                <EventTrigger.Actions>
                                    <BeginStoryboard>
                                        <Storyboard Name="themeSelectionAnimation">
                                            <DoubleAnimation 
                                                Storyboard.TargetName="coloredRectangle"
                                                Storyboard.TargetProperty="Width"
                                                From="30.0" 
                                                To="250.0" 
                                                Duration="0:0:0.3" />
                                        </Storyboard>
                                    </BeginStoryboard>                                    
                                </EventTrigger.Actions>
                            </EventTrigger>
                        </ControlTemplate.Triggers>
                        <Canvas HorizontalAlignment="Stretch" ClipToBounds="False" >
                            <ContentPresenter Canvas.Left="{Binding MenuLayout.MenuItemLeftMargin}" HorizontalAlignment="Center"                                                  
                                            VerticalAlignment="Center" Canvas.ZIndex="1"/>
                            <TextBlock 
                                Text="{Binding SecondaryText}" 
                                Canvas.Top="50"
                                Canvas.Left="10"
                                FontSize="30"
                                FontWeight="ExtraLight"
                                FontStyle="Italic"
                                Canvas.ZIndex="1"
                                />
                            <Rectangle
                                Canvas.Top="30"
                                Canvas.Left="10"
                                Name="coloredRectangle"
                                Width="30"
                                Height="10"
                                Canvas.ZIndex="0"
                                Fill="{Binding Color}"/>
                        </Canvas>                        
                    </ControlTemplate>                    
                </Setter.Value>
            </Setter>             
        </Style>
        <Storyboard x:Key="themeUnselectionAnimation">
            <DoubleAnimation                 
                Storyboard.TargetProperty="Width"
                From="250.0" 
                To="30.0" 
                Duration="0:0:0.15" />
        </Storyboard>
    </UserControl.Resources>
    <ItemsControl Name="menuButtonContainer" ItemsSource="{Binding Items}" Margin="{Binding MenuLayout.MenuMargin}">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <StackPanel Orientation="Vertical" />
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>        
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <Button 
                    Style="{StaticResource ResourceKey=MenuItemButtonStyle}" 
                    Margin="{Binding ElementName=menuButtonContainer, 
                                        Path=DataContext.MenuLayout.MenuItemMargin}"                    
                    Height="{Binding ElementName=menuButtonContainer, 
                                        Path=DataContext.MenuLayout.MenuItemSize.Height}"
                    Content="{Binding Text}"                    
                    Command="{Binding ElementName=menuButtonContainer, 
                                        Path=DataContext.ChangeThemeCommand}"
                    CommandParameter="{Binding Id}"
                    />
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
</UserControl>

正如您在上面的代码中看到的,我在按钮点击时自动触发动画。我想在单击集合中的另一个按钮时反向播放(选择另一个菜单项)。要播放的动画是名为themeUnselectionAnimation的动画。

第一个问题:有没有办法只在XAML中做到这一点?我不确定是否需要按下另一个按钮来触发它。

以下是我的想法:

  1. Button(这是我的菜单项)命令操作中,发送一条消息,让订阅者更改菜单项。
  2. MainMenuView代码隐藏中注册。
  3. 从那里发布动画。
  4. 到目前为止,我的问题是为动画设置目标控件。为此,我需要在Rectangle中找到名为coloredRectangle的{​​{1}}。怎么做?

    以下是我对应上述步骤的代码:

    第1步:发送消息(我正在使用MVVM Light框架)

    ControlTemplate

    第2步:注册消息

    /// <summary>
    /// Delegates that handles theme change process and tasks
    /// </summary>
    /// <param name="themeId">the new active theme</param>
    private void ChangeTheme(int themeId)
    {
        // Set current active theme as inactive, if one is selected.
        // Exception use because of Single implementation that throw an InvalidOperationException if not item is found
        try
        {
            MenuItem currentTheme = Items.Single(x => x.IsActive == true);
    
            // Check if this is current theme. If it is, we do nothing.
            if(currentTheme.Id == themeId)
                return;
    
            // If current theme is set and new theme id is not the same, disable the old one
            currentTheme.IsActive = false;
            // Set new theme as active
            Items.Single(x => x.Id == themeId).IsActive = true;
    
            // Finally, launch unselection animation
    
            // Send message and register to it in view code behind
            // Create inner message
            ThemeChangeNotification innerMessage = new ThemeChangeNotification();
            innerMessage.NewThemeId = themeId;
            innerMessage.OldThemeId = currentTheme.Id;
    
            NotificationMessage<ThemeChangeNotification> message =
                new NotificationMessage<ThemeChangeNotification>(innerMessage, "");
            // Send message
            Messenger.Default.Send(message);                
        }
        catch (InvalidOperationException exception)
        {
            // Set first theme selection as active
            Items.Single(x => x.Id == themeId).IsActive = true;
        }                                    
    }
    

    第3步:从index / id到达Button并启动动画(不工作)

    Messenger.Default.Register<NotificationMessage<ThemeChangeNotification>>(this, ChangeThemeAnimation);
    

    非常感谢您的回答!

1 个答案:

答案 0 :(得分:1)

当然,您可以根据使用另一个Style的第一个来创建另一个Storyboard ...然后您可以在{{1}上应用反向Style ()你想要开始Button

Storyboard

我必须完全诚实......我并不完全理解你的问题,所以如果没有回答,似乎你也想知道如何开始<Style x:Key="ReverseMenuItemButtonStyle" TargetType="Button"> <Setter Property="FontSize" Value="60" /> <Setter Property="FontFamily" Value="Segoe" /> <Setter Property="FontWeight" Value="UltraLight" /> <Setter Property="Foreground" Value="#FFEBEDEA" /> <Setter Property="HorizontalContentAlignment" Value="Right" /> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type Button}"> <ControlTemplate.Triggers> <EventTrigger RoutedEvent="Button.Click"> <EventTrigger.Actions> <BeginStoryboard> <Storyboard Name="themeUnselectionAnimation"> <DoubleAnimation Storyboard.TargetName="coloredRectangle" Storyboard.TargetProperty="Width" From="30.0" To="250.0" Duration="0:0:0.3" /> </Storyboard> </BeginStoryboard> </EventTrigger.Actions> </EventTrigger> </ControlTemplate.Triggers> ... </ControlTemplate> </Setter.Value> </Setter> </Style> 从视图模型。在这种情况下,您只需要一个Storyboard属性,该属性将在视图模型中设置为bool时启动动画。您可以使用true执行此操作:

DataTrigger.EnterActions

更新&gt;&gt;&gt;

再次......我仍然不知道你在追求什么...我建议在发布另一个问题之前先处理你的问题。但是,我能解决这个问题:

  

您的 TargetName无法用于Style Setter 错误,并且您希望定位<Style> <Style.Triggers> <DataTrigger Binding="{Binding SomeBooleanPropertyInViewModel}" Value="True"> <DataTrigger.EnterActions> <BeginStoryboard> <Storyboard ... /> </BeginStoryboard> </DataTrigger.EnterActions> </DataTrigger> </Style.Triggers> </Style> 元素。

此错误的常见修复方法是将coloredRectangle移动到您尝试定位的元素。所以试试这个:

Trigger