[英]WPF Launch animation from code-behind in MVVM
I'm looking for the way to code-behind launch an animation on a control contained in an ControlTemplate
. 我正在寻找在
ControlTemplate
包含的ControlTemplate
上进行代码隐藏启动动画的方法。 In my app I have custom templated buttons (playing menu role) created from an ObservableCollection
: 在我的应用程序中,我具有从
ObservableCollection
创建的自定义模板化按钮(播放菜单角色):
MainMenuViewModel : 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 : 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.
要播放的动画是一种称为themeUnselectionAnimation的动画。
First question : is there a way to do it only in XAML? 第一个问题:仅在XAML中有方法吗? I'm not sure as another button need to be pressed to trigger it.
我不确定是否需要按下另一个按钮来触发它。
Below is what I thought : 以下是我的想法:
Button
(which is my menu item) command action, send a message to let subscribers that menu item is changing. Button
(这是我的菜单项)命令操作中,发送一条消息以使订阅者该菜单项正在更改。 MainMenuView
code-behind. MainMenuView
注册它。 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
. 为此,我需要在
ControlTemplate
找到名为coloredRectangle
的Rectangle
。 How to do that ? 怎么做 ?
Here below is my code corresponding to above steps : 以下是与上述步骤对应的代码:
Step 1 : send message (I'm using MVVM Light framework) 步骤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;
}
}
Step 2 : Register to message 第2步:注册到邮件
Messenger.Default.Register<NotificationMessage<ThemeChangeNotification>>(this, ChangeThemeAnimation);
Step 3 : reach Button from index/id and launch animation (not working) 步骤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();
}
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
: 当然,您可以基于第一个使用另一个
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>
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. 我必须完全诚实...我不完全理解您的问题,因此,如果没有回答,您似乎也想知道如何从视图模型启动
Storyboard
。 In this case, you just need a bool
property which will start the animation when set to true
in the view model. 在这种情况下,您只需要一个
bool
属性,当在视图模型中将其设置为true
,该属性将启动动画。 You can do that using the DataTrigger.EnterActions
: 您可以使用
DataTrigger.EnterActions
做到这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.您将收到无法在Style Setter错误上使用TargetName的信息,并且希望将自己的
coloredRectangle
元素作为目标。
The usual fix for this error is simply to move your Trigger
to the element that you are trying to target. 通常,此错误的解决方法是将“
Trigger
移动到您要定位的元素。 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>
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.