繁体   English   中英

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

[英]WPF Launch animation from code-behind in MVVM

我正在寻找在ControlTemplate包含的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. 从那里启动动画。

到目前为止,我的问题是为动画设置目标控件。 为此,我需要在ControlTemplate找到名为coloredRectangleRectangle 怎么做 ?

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

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

/// <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;
    }                                    
}

第2步:注册到邮件

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

步骤3:从索引/ ID到达Button并启动动画(不起作用)

/// <summary>
/// Theme change message delegate
/// </summary>
/// <param name="e">The ThemeChangeNotification message</param>
private void ChangeThemeAnimation(NotificationMessage<ThemeChangeNotification> message)
{
    var buttonTheme = menuButtonContainer.ItemContainerGenerator.ContainerFromIndex(message.Content.OldThemeId) as FrameworkElement;
    var rectangle = buttonTheme.FindName("coloredRectangle") as Rectangle;
    Storyboard sb = this.FindResource("themeUnselectionAnimation") as Storyboard;
    Storyboard.SetTarget(sb, rectangle);
    sb.Begin();
}

非常感谢您的回答!

当然,您可以基于第一个使用另一个Storyboard Style来创建另一个Style ...然后,您可以将反向Style应用于要启动该Storyboard任何Button

<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做到这DataTrigger.EnterActions

<Style>
    <Style.Triggers>
        <DataTrigger Binding="{Binding SomeBooleanPropertyInViewModel}" Value="True">
            <DataTrigger.EnterActions>
                <BeginStoryboard>
                    <Storyboard ... />
                </BeginStoryboard>
            </DataTrigger.EnterActions>
        </DataTrigger>
    </Style.Triggers>
</Style>

更新>>>

再说一次……我仍然不太清楚你在追求什么……我建议在发布另一个问题之前先研究您的提问技巧。 但是,我可以解决很多问题:

您将收到无法在Style Setter错误上使用TargetName的信息,并且希望将自己的coloredRectangle元素作为目标。

通常,此错误的解决方法是将“ Trigger移动到您要定位的元素。 因此,请尝试以下操作:

<Rectangle Canvas.Top="30" Canvas.Left="10" Name="coloredRectangle" ... >
    <Rectangle.Style>
        <Style>
            <Style.Triggers>
                <DataTrigger Binding="{Binding SomeBooleanProperty}" Value="True">
                    <DataTrigger.EnterActions>
                        <BeginStoryboard>
                            <Storyboard ... />
                        </BeginStoryboard>
                    </DataTrigger.EnterActions>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </Rectangle.Style>
</Rectangle>

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM