从$ C $ WPF启动动画C-背后MVVM [英] WPF Launch animation from code-behind in MVVM

查看:314
本文介绍了从$ C $ WPF启动动画C-背后MVVM的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在寻找的路上code-背后包含在控件模板控制推出的动画。
在我的应用我有自定义的模板按钮(播放菜单角色)从创建了一个的ObservableCollection

MainMenuViewModel:

  ///<总结>
///菜单项列表
///< /总结>
私人的ObservableCollection<&菜单项GT; _items;....///<总结>
///菜单项列表属性
///< /总结>
公众的ObservableCollection<&菜单项GT;项目
{
    {返回_items; }
    集合{_items =价值; }
}

MainMenuView:

 <用户控件X:类=OfficeTourismeBrantome.Views.MainMenuView
         的xmlns =htt​​p://schemas.microsoft.com/winfx/2006/xaml/$p$psentation
         的xmlns:X =htt​​p://schemas.microsoft.com/winfx/2006/xaml
         的xmlns:MC =htt​​p://schemas.openxmlformats.org/markup-compatibility/2006
         的xmlns:D =htt​​p://schemas.microsoft.com/ex$p$pssion/blend/2008
         MC:可忽略=D
         D:DesignHeight =800D:DesignWidth =300>
    < UserControl.Resources>
        <风格X:键=MenuItemButtonStyle的TargetType =按钮>
            < setter属性=字号VALUE =60/>
            < setter属性=的FontFamilyVALUE =濑越/>
            < setter属性=粗细VALUE =超轻/>
            < setter属性=前景VALUE =#FFEBEDEA/>
            <! - < setter属性=高度值={结合MenuLayout.MenuItemSize.Height}/> - >
            < setter属性=Horizo​​ntalContentAlignmentVALUE =右/>
            < setter属性=模板>
                < Setter.Value>
                    <的ControlTemplate的TargetType ={X:类型按钮}>
                        < ControlTemplate.Triggers>
                            <的EventTrigger RoutedEvent =Button.Click>
                                < EventTrigger.Actions>
                                    < BeginStoryboard>
                                        <情节提要NAME =themeSelectionAnimation>
                                            < D​​oubleAnimation是
                                                Storyboard.TargetName =coloredRectangle
                                                Storyboard.TargetProperty =宽度
                                                从=30.0
                                                为了=250.0
                                                持续时间=0:0:0.3/>
                                        < /故事板>
                                    < / BeginStoryboard>
                                < /EventTrigger.Actions>
                            < /&的EventTrigger GT;
                        < /ControlTemplate.Triggers>
                        <帆布的Horizo​​ntalAlignment =拉伸ClipToBounds =FALSE>
                            <内容presenter Canvas.Left ={结合MenuLayout.MenuItemLeftMargin}的Horizo​​ntalAlignment =中心
                                            VerticalAlignment =中心Canvas.ZIndex =1/>
                            <的TextBlock
                                文本={结合SecondaryText}
                                Canvas.Top =50
                                Canvas.Left =10
                                字号=30
                                粗细=ExtraLight
                                fontstyle的=斜体
                                Canvas.ZIndex =1
                                />
                            <矩形
                                Canvas.Top =30
                                Canvas.Left =10
                                NAME =coloredRectangle
                                宽度=30
                                高度=10
                                Canvas.ZIndex =0
                                填写={结合颜色}/>
                        < /帆布>
                    < /控件模板>
                < /Setter.Value>
            < /二传手>
        < /样式和GT;
        <情节提要X:键=themeUnselectionAnimation>
            < D​​oubleAnimation是
                Storyboard.TargetProperty =宽度
                从=250.0
                为了=30.0
                持续时间=0:0:0.15/>
        < /故事板>
    < /UserControl.Resources>
    < ItemsControl的名称=menuButtonContainer的ItemsSource ={绑定表项}保证金={结合MenuLayout.MenuMargin}>
        < ItemsControl.ItemsPanel>
            < ItemsPanelTemplate>
                < StackPanel的方向=垂直/>
            < / ItemsPanelTemplate>
        < /ItemsControl.ItemsPanel>
        < ItemsControl.ItemTemplate>
            <&DataTemplate的GT;
                <按钮
                    风格={StaticResource的的ResourceKey = MenuItemButtonStyle}
                    保证金={绑定的ElementName = menuButtonContainer,
                                        路径= DataContext.MenuLayout.MenuItemMargin}
                    HEIGHT ={绑定的ElementName = menuButtonContainer,
                                        路径= DataContext.MenuLayout.MenuItemSize.Height}
                    CONTENT ={结合文字}
                    命令={绑定的ElementName = menuButtonContainer,
                                        路径= DataContext.ChangeThemeCommand}
                    CommandParameter ={绑定ID}
                    />
            < / DataTemplate中>
        < /ItemsControl.ItemTemplate>
    < / ItemsControl的>
< /用户控件>

正如你可以在上面的code看到,我得到了一个动画按钮点击自动触发。我要玩集合中的另一个按钮被点击时(选择了另一个菜单项),它反转。播放的动画是一个叫themeUnselectionAnimation。

第一个问题:有没有办法做到这一点只在XAML?我不知道作为另一个按钮必须pssed来触发它$ P $。

下面是我想:


  1. 按钮(这是我的菜单项)命令的动作,将消息发送给用户,让该菜单项正在发生变化。

  2. 注册以它 MainMenuView code-落后。

  3. 启动动画从那里。

我的问题,到目前为止是设置动画的目标控制。要做到这一点,我需要找到长方形名为 coloredRectangle 控件模板。那怎么办?

下面下面是对应于以上步骤,我的code:

第1步:发送短信(我使用MVVM轻框架)

  ///<总结>
///代表处理主题变换过程和任务
///< /总结>
///< PARAM NAME =的ThemeID>在新的活动主题< /参数>
私人无效ChangeTheme(INT的ThemeID)
{
    //设置当前活动的主题为不活动,如果选择之一。
    //异常使用,因为单执行如果没有项目被发现,引发InvalidOperationException
    尝试
    {
        菜单项currentTheme = Items.Single(X => x.IsActive == TRUE);        //检查,如果这是当前的主题。如果是,我们什么也不做。
        如果(currentTheme.Id ==的ThemeID)
            返回;        //如果当前主题设置和新的主题ID是不一样的,禁用旧
        currentTheme.IsActive = FALSE;
        //设置新的主题为活动
        Items.Single(X => x.Id ==的ThemeID).IsActive = TRUE;        //最后,启动非选择动画        //鉴于code发送信息并注册其背后
        //创建内部消息
        ThemeChangeNotification innerMessage =新ThemeChangeNotification();
        innerMessage.NewThemeId =的ThemeID;
        innerMessage.OldThemeId = currentTheme.Id;        NotificationMessage< ThemeChangeNotification>消息=
            新NotificationMessage&所述; ThemeChangeNotification>(innerMessage,);
        // 发信息
        Messenger.Default.Send(消息);
    }
    赶上(InvalidOperationException异常)
    {
        //设置第一个主题选择为活动
        Items.Single(X => x.Id ==的ThemeID).IsActive = TRUE;
    }
}

第二步:注册信息

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

第三步:从指数/ ID和发射动画达到按钮(不工作)

  ///&LT;总结&gt;
///主题变化信息的委托
///&LT; /总结&gt;
///&LT; PARAM NAME =E&gt;该ThemeChangeNotification消息&LT; /参数&GT;
私人无效ChangeThemeAnimation(NotificationMessage&LT; ThemeChangeNotification&gt;消息)
{
    VAR buttonTheme = menuButtonContainer.ItemContainerGenerator.ContainerFromIndex(message.Content.OldThemeId)为FrameworkElement的;
    VAR矩形= buttonTheme.FindName(coloredRectangle)的矩形;
    故事板SB = this.FindResource(themeUnselectionAnimation)作为故事板;
    Storyboard.SetTarget(SB,矩形);
    sb.Begin();
}

非常感谢你对你的答案!


解决方案

当然,你可以创建另一个风格基于使用其他的第一个故事板,而不是...那么你可以只申请反向风格上取按钮(S)要启动故事板

 &LT;风格X:键=ReverseMenuItemButtonStyle的TargetType =按钮&GT;
    &LT; setter属性=字号VALUE =60/&GT;
    &LT; setter属性=的FontFamilyVALUE =濑越/&GT;
    &LT; setter属性=粗细VALUE =超轻/&GT;
    &LT; setter属性=前景VALUE =#FFEBEDEA/&GT;
    &LT; setter属性=Horizo​​ntalContentAlignmentVALUE =右/&GT;
    &LT; setter属性=模板&GT;
        &LT; Setter.Value&GT;
            &LT;的ControlTemplate的TargetType ={X:类型按钮}&GT;
                &LT; ControlTemplate.Triggers&GT;
                    &LT;的EventTrigger RoutedEvent =Button.Click&GT;
                        &LT; EventTrigger.Actions&GT;
                            &LT; BeginStoryboard&GT;
                                &LT;情节提要NAME =themeUnselectionAnimation&GT;
                                    &LT; D​​oubleAnimation是
                                        Storyboard.TargetName =coloredRectangle
                                        Storyboard.TargetProperty =宽度
                                        从=30.0
                                        为了=250.0
                                        持续时间=0:0:0.3/&GT;
                                &LT; /故事板&GT;
                            &LT; / BeginStoryboard&GT;
                        &LT; /EventTrigger.Actions>
                    &LT; /&的EventTrigger GT;
                &LT; /ControlTemplate.Triggers>
                ...
            &LT; /控件模板&GT;
        &LT; /Setter.Value>
    &LT; /二传手&GT;
&LT; /样式和GT;

我得完全说实话...我没有完全理解你的问题,因此,如果这没有回答它,它好像你也想知道如何开始一个故事板从视图模型。在这种情况下,你只需要一个布尔属性时,设置为这将启动动画真正视图模式。你可以做,使用 DataTrigger.EnterActions

 &LT;样式和GT;
    &LT; Style.Triggers&GT;
        &LT; D​​ataTrigger绑定={结合SomeBooleanPropertyInViewModel}VALUE =真&GT;
            &LT; D​​ataTrigger.EnterActions&GT;
                &LT; BeginStoryboard&GT;
                    &LT;情节提要... /&GT;
                &LT; / BeginStoryboard&GT;
            &LT; /DataTrigger.EnterActions>
        &LT; / DataTrigger&GT;
    &LT; /Style.Triggers>
&LT; /样式和GT;


更新>>>

再次...我还是真的不知道你在做什么......以后我会建议的工作对你的问题问的技巧发布另一个之前。不过,这一点我可以工作了:


  

您会得到一个的的TargetName不能在风格二传手使用的错误,并要针对您的 coloredRectangle 元素。


这个错误通常的处理办法很简单,就是将你的触发来,你是努力的目标元素。因此,尝试这个:

 &LT;矩形Canvas.Top =30Canvas.Left =10NAME =coloredRectangle...&GT;
    &LT; Rectangle.Style&GT;
        &LT;样式和GT;
            &LT; Style.Triggers&GT;
                &LT; D​​ataTrigger绑定={结合SomeBooleanProperty}VALUE =真&GT;
                    &LT; D​​ataTrigger.EnterActions&GT;
                        &LT; BeginStoryboard&GT;
                            &LT;情节提要... /&GT;
                        &LT; / BeginStoryboard&GT;
                    &LT; /DataTrigger.EnterActions>
                &LT; / DataTrigger&GT;
            &LT; /Style.Triggers>
        &LT; /样式和GT;
    &LT; /Rectangle.Style>
&LT; /矩形&GT;

I'm looking for the way to code-behind launch an animation on a control contained in an ControlTemplate. In my app I have custom templated buttons (playing menu role) created from an 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>

As you can see in the code above, I got an animation automatically triggered on button click. I want to play it reverse when another button in the collection is clicked (another menu entry is selected). The animation to play is the one called themeUnselectionAnimation.

First question : is there a way to do it only in XAML? I'm not sure as another button need to be pressed to trigger it.

Below is what I thought :

  1. In the Button (which is my menu item) command action, send a message to let subscribers that menu item is changing.
  2. Register to it in MainMenuView code-behind.
  3. Launch animation from there.

My problem so far is to set the target control for the animation. To do that, I need to find the Rectangle named coloredRectangle in ControlTemplate. How to do that ?

Here below is my code corresponding to above steps :

Step 1 : send message (I'm using MVVM Light framework)

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

Step 2 : Register to message

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

Step 3 : reach Button from index/id and launch animation (not working)

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

Thank you very much for your answers !

解决方案

Surely, you can just create another Style based on the first one that uses the other Storyboard instead... then you could just apply the reverse Style on whichever Button(s) that you want to start that 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>

I've got to be fully honest... I didn't totally understand your question, so if that didn't answer it, it seems as though you also want to know how to start a Storyboard from a view model. In this case, you just need a bool property which will start the animation when set to true in the view model. You can do that using the DataTrigger.EnterActions:

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


UPDATE >>>

Again... I still don't really know what you're after... I'd suggest working on your question asking skills before posting another one. However, this much I could work out:

You're getting an TargetName cannot be used on Style Setter error and you want to target your coloredRectangle element.

The usual fix for this error is simply to move your Trigger to the element that you are trying to target. So try this instead:

<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>

这篇关于从$ C $ WPF启动动画C-背后MVVM的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆