从$ C $ WPF启动动画C-背后MVVM [英] WPF Launch animation from code-behind in MVVM
问题描述
我在寻找的路上code-背后包含在控件模板
控制推出的动画。
在我的应用我有自定义的模板按钮(播放菜单角色)从创建了一个的ObservableCollection
:
MainMenuViewModel:
///<总结>
///菜单项列表
///< /总结>
私人的ObservableCollection<&菜单项GT; _items;....///<总结>
///菜单项列表属性
///< /总结>
公众的ObservableCollection<&菜单项GT;项目
{
{返回_items; }
集合{_items =价值; }
}
MainMenuView:
<用户控件X:类=OfficeTourismeBrantome.Views.MainMenuView
的xmlns =http://schemas.microsoft.com/winfx/2006/xaml/$p$psentation
的xmlns:X =http://schemas.microsoft.com/winfx/2006/xaml
的xmlns:MC =http://schemas.openxmlformats.org/markup-compatibility/2006
的xmlns:D =http://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属性=HorizontalContentAlignmentVALUE =右/>
< setter属性=模板>
< Setter.Value>
<的ControlTemplate的TargetType ={X:类型按钮}>
< ControlTemplate.Triggers>
<的EventTrigger RoutedEvent =Button.Click>
< EventTrigger.Actions>
< BeginStoryboard>
<情节提要NAME =themeSelectionAnimation>
< DoubleAnimation是
Storyboard.TargetName =coloredRectangle
Storyboard.TargetProperty =宽度
从=30.0
为了=250.0
持续时间=0:0:0.3/>
< /故事板>
< / BeginStoryboard>
< /EventTrigger.Actions>
< /&的EventTrigger GT;
< /ControlTemplate.Triggers>
<帆布的HorizontalAlignment =拉伸ClipToBounds =FALSE>
<内容presenter Canvas.Left ={结合MenuLayout.MenuItemLeftMargin}的HorizontalAlignment =中心
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>
< DoubleAnimation是
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 $。
下面是我想:
- 在
按钮
(这是我的菜单项)命令的动作,将消息发送给用户,让该菜单项正在发生变化。 - 注册以它
MainMenuView
code-落后。 - 启动动画从那里。
我的问题,到目前为止是设置动画的目标控制。要做到这一点,我需要找到长方形
名为 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属性=HorizontalContentAlignmentVALUE =右/&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; DoubleAnimation是
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; DataTrigger绑定={结合SomeBooleanPropertyInViewModel}VALUE =真&GT;
&LT; DataTrigger.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; DataTrigger绑定={结合SomeBooleanProperty}VALUE =真&GT;
&LT; DataTrigger.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 anObservableCollection
: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 :
- In the
Button
(which is my menu item) command action, send a message to let subscribers that menu item is changing.- Register to it in
MainMenuView
code-behind.- 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
namedcoloredRectangle
inControlTemplate
. 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 otherStoryboard
instead... then you could just apply the reverseStyle
on whicheverButton
(s) that you want to start thatStoryboard
:<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 abool
property which will start the animation when set totrue
in the view model. You can do that using theDataTrigger.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屋!