Can a storyboard be placed in a resource file such as styles.xaml? I have a toolbar that will be reused across many pages. I have this working now with a page level resource:
<navigation:Page.Resources>
<Storyboard x:Name="sbToolbarInitialization">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.TranslateY)"
Storyboard.TargetName="Toolbar">
<EasingDoubleKeyFrame KeyTime="0"
Value="46" />
<EasingDoubleKeyFrame KeyTime="0:0:1"
Value="0">
<EasingDoubleKeyFrame.EasingFunction>
<BackEase EasingMode="EaseOut" />
</EasingDoubleKeyFrame.EasingFunction>
</EasingDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</navigation:Page.Resources>
Which the border control uses:
<Border x:Name="Toolbar"
Style="{StaticResource ToolbarBorderStyle}">
<Border.RenderTransform>
<CompositeTransform />
</Border.RenderTransform>
<i:Interaction.Triggers>
<i:EventTrigger>
<ei:ControlStoryboardAction Storyboard="{StaticResource sbToolbarInitialization}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Border>
Since I am already using the style.xaml file to place formatting for the border I would like to also store the storyboard there. Is this possible?
I've done something similar, storing the Storyboard in my App.xaml file and using across my entire application. One way to access it in your code-behind or view-model is as such:
public Storyboard MyStoryBoard = Application.Current.Resources["MyStoryBoard "] as Storyboard;
You could then bind that storyboard property to your view declaratively.
Related
<VisualState x:Name="MouseOver">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Bd"
Storyboard.TargetProperty="Fill">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource
ButtonHoverBackgroundBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Bd"
Storyboard.TargetProperty="Stroke">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource
ButtonHoverBorderBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Duration="0"
Storyboard.TargetProperty="Foreground"
Storyboard.TargetName="ContentControl">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource
LightForegroundBrush}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
Instead of going Storyboard.TargetName=ContentControl and Storyboard.TargetProperty=Foreground,
i would like to Animate the templated parent's foreground, and the content control to just have it's foreground property binded to the templated parent
Foreground={TemplateBinding Foreground}
The reason for this is that when i use the control with this template, i want to be able to do :
<Button Width="125"
Height="30"
Click="OnButtonClick"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Margin="50,54,0,0">
<Button.Content>
<controls:SomeCustomControl '
Foreground="{Binding Path=Foreground,
RelativeSource={RelativeSource AncestorType=
{x:Type Button}}}" />
</Button.Content>
</Button>
I tried doing :
<ObjectAnimationUsingKeyFrames Duration="0" Storyboard.Target="{Binding Path=Foreground, RelativeSource={RelativeSource TemplatedParent}}">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource LightForegroundBrush}" />
</ObjectAnimationUsingKeyFrames>
however this crashes.
Any suggestions on how this can be done? Thanks in advance.
Same Problem on my Side ....
But it could be that we just thought in the wrong way.
It is "Content" to be styled but no one knows what Content it would be. So the "right" way would be to say that its the work for the Control you put into the Content.
Example, if you put a Button into a ContentControl, the Button style handle the VSM not the ContentControl around it.
That ist not the exactly answer to your question but maybe some help to understand why this isnĀ“t some option.
Edit:
After some research i found this:
Animating a TextBox.Foreground in WPF
It feels not the best practice but it could be work for your problem.
I'm pulling my hair out trying to get this to work. Im trying to learn to do transitions and struggling a bit. Basically im making a combo box, made up of a grid containing 2 rows. top row is a button, when clicked the bottom row opens up showing a scrollviewer of dynamically added buttons. When clicked, the bottom grid row will collapse.
The problem is that the Click event does not seem to fire from within the scroll viewer, or it can't find the visual state in the scope or something.
So it the SelectionMode works fine when Button11 is clicked, but nothing happens when I click on an item button. The buttons is the scrollviewer work perfectly with their own color animations and firing events etc
Id be open to a solution in code-behind since I can make routed click event fire no problem, but I had had no luck with
VisualStateManager.GoToState(gridContent, "HiddenMode", true);
Ideally I'd like this to be a custom user control I can add like local:CustomComboBox but It complicated for me at this stage having controls inside controls in a custom control.
MyButton1 is just a simple button but with color transitions etc
No exceptions / errors just nothing happens when I click the button
<Window.Resources>
<Storyboard x:Key="sb1">
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="gridContent" Storyboard.TargetProperty="Height">
<EasingDoubleKeyFrame KeyTime="0" Value="30" />
<EasingDoubleKeyFrame KeyTime="0:0:1" Value="160" />
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="scrItems" Storyboard.TargetProperty="Height">
<EasingDoubleKeyFrame KeyTime="0" Value="0" />
<EasingDoubleKeyFrame KeyTime="0:0:1" Value="130" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Key="sb2">
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="gridContent" Storyboard.TargetProperty="Height">
<EasingDoubleKeyFrame KeyTime="0" Value="160" />
<EasingDoubleKeyFrame KeyTime="0:0:1" Value="30" />
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="scrItems" Storyboard.TargetProperty="Height">
<EasingDoubleKeyFrame KeyTime="0" Value="130" />
<EasingDoubleKeyFrame KeyTime="0:0:1" Value="0" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</Window.Resources>
<Grid Name="Grid1" Margin="0,22,0,0" RenderTransformOrigin="0.5,0.5">
<Grid Name="gridContent" HorizontalAlignment="Left" Margin="187,74,0,0" VerticalAlignment="Top" Width="140" Height="30" RenderTransformOrigin="0.5,0.5">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="VisualStateGroup">
<VisualState x:Name="SelectionMode" Storyboard="{StaticResource sb1}" />
<VisualState x:Name="HiddenMode" Storyboard="{StaticResource sb2}" />
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid.RowDefinitions>
<RowDefinition Height="30"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<local1:Button1 Name="Button11" Content="Click" Height="30" Grid.Row="0" Margin="0,0,0,30">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<ei:GoToStateAction TargetName="gridContent" StateName="SelectionMode" />
</i:EventTrigger>
</i:Interaction.Triggers>
</local1:Button1>
<ScrollViewer Name="scrItems" VerticalScrollBarVisibility="Hidden" HorizontalScrollBarVisibility="Hidden" Margin="0" Width="140" Height="0" Grid.Row="1">
<ItemsControl x:Name="stkItems">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<local1:Button1 Content="Click" Height="30">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<ei:GoToStateAction TargetName="gridContent" StateName="HiddenMode" />
</i:EventTrigger>
</i:Interaction.Triggers>
</local1:Button1>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</Grid>
</Grid>
private void Window_Loaded(object sender, RoutedEventArgs e)
{
lstItems = new ObservableCollection<MyButton.Button1>();
for (int i = 0; i <= 999; i++)
{
MyButton.Button1 item1 = new Button1();
item1.Content = "Item " + i;
item1.Width = stkItems.Width;
item1.Height = 30;
//item1.Click += new RoutedEventHandler(Button_Item_Click);
lstItems.Add(item1);
}
stkItems.DataContext = this.stkItems;
stkItems.ItemsSource = lstItems;
}
SOLVED!!!
I moved the visualStateManager to the root element - Grid1, but dont know if that affects it
private void Button_Item_Click(object sender, RoutedEventArgs e)
{
bool g = ExtendedVisualStateManager.GoToElementState(this.Grid1 as FrameworkElement, "HiddenMode", true);
}
I have a WPF-View with an ItemsControl that binds an ObservableCollection from the ViewModel. Now i want to fade out slowly items which i remove from the ObservableCollection.
ViewModel:
public class ViewModel
{
public ObservableCollection<string> Items { get; set; }
}
View:
<Window x:Class="Sandbox.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="350" Width="525"
Name="mainWindow">
<Window.Resources>
<DataTemplate x:Key="mytemplate">
<DataTemplate.Resources>
<Storyboard x:Key="OnUnloaded">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)" Storyboard.TargetName="grid">
<EasingDoubleKeyFrame KeyTime="0" Value="1"/>
<EasingDoubleKeyFrame KeyTime="0:0:0.3" Value="-0"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.Y)" Storyboard.TargetName="grid">
<EasingDoubleKeyFrame KeyTime="0" Value="0"/>
<EasingDoubleKeyFrame KeyTime="0:0:0.3" Value="-10"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</DataTemplate.Resources>
<Grid x:Name="grid" RenderTransformOrigin="0.5,0.5">
<Grid.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform/>
<RotateTransform/>
<TranslateTransform/>
</TransformGroup>
</Grid.RenderTransform>
<TextBox Text="{Binding Mode=OneWay}"/>
</Grid>
<DataTemplate.Triggers>
<EventTrigger RoutedEvent="FrameworkElement.Unloaded">
<BeginStoryboard Storyboard="{StaticResource OnUnloaded}"/>
</EventTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</Window.Resources>
<StackPanel>
<ItemsControl Grid.Row="1" ItemsSource="{Binding Items}" ItemTemplate="{StaticResource mytemplate}"/>
</StackPanel>
The problem i have is now, that the storyboard begins by removing an item from the collection, but at the same time the itemscontrol remove the item and for this, the animation is not in sight...
any idea how to prevent remove the item before animation terminate?
This is much more difficult than it should be. The problem with a "remove" animation is that once an item is removed from the databound collection, its corresponding visuals are automatically removed from the element tree. This means that there is nothing left to animate out.
To work around it, you need to find a way to queue the animation before you remove the item from the databound collection and once the animation is complete, notify the ViewModel that it is okay to remove the item.
The other solution is to modify the behavior of the ItemsControl to better control the lifetime of the container visuals.
Either way, it is unfortunately not a trivial task to accomplish.
i will post my source code i have at the moment and explain my problem after that.
this is the window where i want the transition to happen
<Window x:Class="MyApp.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MyApp" Height="600" Width="800">
<StackPanel>
<Button Content="View1" Command="{Binding View1Command}"/>
<Button COntent="View2" Command="{Binding View2Command}"/>
<ContentControl Content="{Binding MyContent}"/>
</StackPanel>
This is the associated View-Model
public class MainViewModel
{
public RelayCommand View1Command{ get; private set;}
public RelayCommand View2Command{ get; private set;}
//INotifyPropertyChanged is implemented of course
public ViewModelBase MyContent { get; set;}
public MainViewModel()
{
View1Command = new RelayCommand(() => { MyContent = new ViewModel1();});
View2Command = new RelayCommand(() => { MyContent = new ViewModel2();});
}
}
In the app.xaml i used a data template to associate ViewModel1 with View1 and ViewModel2 with View2. That all works as expected. When clicking the buttons, the view changes, databinding works and everything is ok.
What i like to do now is to use an animation when the view changes.
what do i have to do to animate the transition between the two views with let's say a fade in animation for the new view. the old view disappears and the new one is faded in within one second.
hope you can help me.
best regards
pascal
p.s. if my approach with the data template does not make sense and animation is impossible with that approach i am open for other best practices to change from onw view to another. the solution i use is taken from the wpf demo app by karl shifflet, but i have no idea if that's the right solution for the thing i try to do.
<Window.Resources>
<Storyboard x:Key="OnClick1">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="CC1">
<EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="1"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="CC2">
<EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="0"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Key="OnClick2">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="CC1">
<EasingDoubleKeyFrame KeyTime="0" Value="0"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="CC2">
<EasingDoubleKeyFrame KeyTime="0" Value="1"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</Window.Resources>
<Window.Triggers>
<EventTrigger RoutedEvent="ButtonBase.Click" SourceName="button">
<BeginStoryboard x:Name="OnClick1_BeginStoryboard" Storyboard="{StaticResource OnClick1}"/>
<BeginStoryboard x:Name="OnClick2_BeginStoryboard" Storyboard="{StaticResource OnClick2}"/>
</EventTrigger>
</Window.Triggers>
<StackPanel>
<Button x:Name="button" Content="View1" Command="{Binding View1Command}"/>
<Button Content="View2" Command="{Binding View2Command}"/>
<Grid>
<ContentControl Name="CC1" Opacity="1" Content="{Binding MyContent}"/>
<ContentControl Name="CC2" Opacity="0" Content="{Binding MyContent}"/>
</Grid>
</StackPanel>
i changed my approach for the transition by using the view model locator for binding view to viewmodel and the visual state manager for transition.
I've made a simple storyboard that takes a particular ListBoxItem and lets it grow by a factor of 1.3. I'd like to add this animation to every ListBoxItem I create dynamically so that it can be activated when it gets a mouse-over, but the storyboard seems to be hardcoded to that first item:
<Storyboard x:Name="ListItem_MouseEntered">
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="RecentNews" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)">
<SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="1.3"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="RecentNews" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)">
<SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="1.3"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
How should I go about duplicating this storyboard and setting the target to every listboxitem?
Cheers
Nik
PS, I believe I have some errors in the animation, don't worry about that, it's not part of my question :-)
You can define a ControlTemplate for ListBoxItem in the Resources section of the UserControl like this:
<ControlTemplate x:Key="LIT" TargetType="ListBoxItem">
<Border x:Name="MainBorder" BorderBrush="Red" BorderThickness="2" Background="Yellow" MouseEnter="Border_MouseEnter">
<Border.Resources>
<Storyboard x:Name="ItemStory">
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="ItemTransform" Storyboard.TargetProperty="ScaleX">
<SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="1.3"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="ItemTransform" Storyboard.TargetProperty="ScaleY">
<SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="1.3"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</Border.Resources>
<Border.RenderTransform>
<ScaleTransform x:Name="ItemTransform" />
</Border.RenderTransform>
<TextBlock Text="{TemplateBinding Content}" />
</Border>
</ControlTemplate>
Handle the MouseEnter event:
private void Border_MouseEnter(object sender, MouseEventArgs e)
{
Border itemBorder = (Border)sender;
Storyboard itemStory = (Storyboard)itemBorder.FindName("ItemStory");
itemStory.Begin();
}
And use it like this in XAML:
<ListBox x:Name="MyList">
<ListBox.Items>
<ListBoxItem Content="Toto 1" Template="{StaticResource LIT}" />
</ListBox.Items>
</ListBox>
Or like this in C#:
MyList.Items.Add(new ListBoxItem()
{
Content="Toto 2",
Template = (ControlTemplate)Resources["LIT"]
});
If you use the visual state manager, you can apply this to all of type:
This shows how to do just that.