1.I cant set the visual state of my textbox, with my c# code the text shows up but the animation does not run, any ideas, or corrections on my c# code would be helpful for me to understand
below is my xaml:
<ControlTemplate x:Name="instructions_text2" TargetType="TextBox">
<Grid>
<TextBlock TextWrapping="Wrap" TextAlignment="Center" HorizontalAlignment="Center" VerticalAlignment="Center" Text="Press Start and drag!" Foreground="#FFCB1717" FontSize="30" FontFamily="AR DELANEY" />
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CustomGroups">
<VisualState x:Name="Blue">
<Storyboard x:Name="Storyboard1" RepeatBehavior="Forever">
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility" >
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<Visibility>Collapsed</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
<DiscreteObjectKeyFrame KeyTime="0:0:0.2">
<DiscreteObjectKeyFrame.Value>
<Visibility>Visible</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="Opacity" >
<EasingDoubleKeyFrame KeyTime="0" Value="1"/>
<EasingDoubleKeyFrame KeyTime="0:0:0.2" Value="0"/>
<EasingDoubleKeyFrame KeyTime="0:0:1" Value="1"/>
<EasingDoubleKeyFrame KeyTime="0:0:2" Value="0"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</ControlTemplate>
c# code:
TextBox instructions = new TextBox();
instructions.Template = Resources["instructions_text2"] as ControlTemplate;
instructions.Width = playArea.ActualWidth;
instructions.Height = playArea.ActualHeight;
VisualStateManager.GoToState(instructions, "Blue", true);
playArea.Children.Add(instructions);
You are trying to change the visual state of the TextBox before it's ready. In fact you are trying to change the state before it's been added to the visual tree.
Change your code to be:
TextBox instructions = new TextBox();
instructions.Template = Resources["instructions_text2"] as ControlTemplate;
instructions.Width = playArea.ActualWidth;
instructions.Height = playArea.ActualHeight;
instructions.Loaded += Instructions_Loaded;
playArea.Children.Add(instructions);
Then in the Loaded handler you can go to the state you want.
private void Instructions_Loaded(object sender, RoutedEventArgs e)
{
var result = VisualStateManager.GoToState(sender as FrameworkElement, "Blue", true);
}
NOTE: There are two versions of VisualStateManager. The one I used is in the namespace System.Windows and it's GoToState takes a FrameworkElement as the first argument - this is the one used by traditional desktop applications. There's also a VisualStateManager in Windows.UI.Xaml that takes a Control as the first argument - this is the one used by new Windows App programs.
As TextBox is a type of control:
private void Instructions_Loaded(object sender, RoutedEventArgs e)
{
var result = VisualStateManager.GoToState(sender as Control, "Blue", true);
}
should work.
Also the ControlTemplate needs a Key, not a Name *:
<ControlTemplate x:Key="instructions_text2" TargetType="TextBox">
The resources dictionary uses the Key value as it's key, so at the moment you're not finding the resource at all so instructions.Template is null.
* this may only be true for desktop applications
Related
Not sure if am framing the question right. So please bear with me if I'm expecting something absurd. I am building a C#/XAML win10 UWP application following the MVVM pattern.
I've some visual states defined for handling wider screens and also some for running some animations. The problem am facing is that, when the visual state to run the animation is called using the VisualStateManager's GoToState method, the setters effected by the VisualState containing the adaptive triggers are lost.
Here's the sample code:
//Defining my grid here
<Grid x:Name="gridNewDrawing" Margin="4">
<Button x:Name="Confirm" Click="Button_Confirm_Click" Width="180" MaxWidth="220" Height="36" HorizontalAlignment="Left" Style="StaticResource StyleButtonGeneral}"/>
<Button x:Name="Cancel" Click="Button_Cancel_Click" Width="180" MaxWidth="220" Height="36" HorizontalAlignment="Left" Style="StaticResource StyleButtonGeneral}"/>
</Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<VisualState x:Name="WideLayoutTrigger">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="640" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="gridNewDrawing.Margin" Value="16" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="AnimationState">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Cancel" Storyboard.TargetProperty="Visibility" Duration="0">
<DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
When the width is above 640px; the margin on the gridNewDrawing switches to 16; but when I explicitly call the animation using
GoToState("AnimationState")
The margin on the grid defaults to 4 once again. Is there any way I can have the changes made by the adaptivetrigger persist when setting other visual styles?
The margin of the grid changes to default again because your VisualState are in same VisualStateGroup. You can set the AnimationState in another VisualStateGroup to maintain changes made by the AdaptiveTrigger.
See the Remarks of VisualStateGroup class.
The set of visual states within each VisualStateGroup should be mutually exclusive in the group. In other words, the control should be using exactly one of the visual states from each of its defined VisualStateGroup groups at all times. Whenever there's a case where a control is intended to be simultaneously in two states, make sure that the two states are in different groups.
So apply the following code should get what you want:
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="WindowStates">
<VisualState x:Name="WideLayoutTrigger">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="640" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="gridNewDrawing.Margin" Value="16" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="OtherStates">
<VisualState x:Name="AnimationState">
<Storyboard>
<ObjectAnimationUsingKeyFrames Duration="0" Storyboard.TargetName="Cancel" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
I have a problem with VisualState in windows phone 8.1, universal app:
I made a simple system to change visual state if screen have a dimension or other... all works well in runtime, but during design, I mean designer, doesn't works.
So I tried VisualStateManager.GoToState(..) but in designer doesn't works, so maybe the solution is to set initial state, but I've tried also with a trigger (beaviour sdk) but ... nothing, so my problem is to have a visual state in designer to easy works with user Interface, in other way set a visual state that works also in designer...
Here's a super simple example
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="VisualStateGroup">
<VisualState x:Name="small" >
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="tmedium">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<Visibility>Collapsed</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="tlarge">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<Visibility>Collapsed</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="medium" />
<VisualState x:Name="large" />
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<TextBlock Name="tsmall" HorizontalAlignment="Left" Margin="36,87,0,0" TextWrapping="Wrap" Text="small" VerticalAlignment="Top"/>
<TextBlock Name="tmedium" HorizontalAlignment="Left" Margin="124,140,0,0" TextWrapping="Wrap" Text="medium" VerticalAlignment="Top"/>
<TextBlock Name="tlarge" HorizontalAlignment="Left" Margin="220,199,0,0" TextWrapping="Wrap" Text="large" VerticalAlignment="Top"/>
this is in code behind:
/// </summary>
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
this.NavigationCacheMode = NavigationCacheMode.Required;
VisualStateManager.GoToState(this, "small", true);
}
when start a small state is set, but in designer not, so how achieve same result?
Thanks
I am trying to delay an animation of a custom control based on a binding value. In the example below, I’d like the animation to start 5 seconds after the “SelectedAndHit” visual state is selected. However, it doesn’t seem possible to use template binding within the VisualStateManage.
Is TemplateBinding supported within the VisualStateManager? Is there any workaround?
<local:ButtonEx x:Name="Button01" AnimationBeginTime="00:00:05" />
public TimeSpan AnimationBeginTime
{
get { return (TimeSpan)base.GetValue(ButtonEx.AnimationBeginTimeProperty); }
set { base.SetValue(ButtonEx.AnimationBeginTimeProperty, value); }
}
public static readonly DependencyProperty AnimationBeginTimeProperty =
DependencyProperty.Register("AnimationBeginTime", typeof(TimeSpan), typeof(ButtonEx), new PropertyMetadata(TimeSpan.Zero));
<Style TargetType="local:ButtonEx">
<!-- ... -->
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:ButtonEx">
<Grid x:Name="Container" RenderTransformOrigin="0.5, 0.5">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="SelectedAndHit">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Border"
Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ButtonBackgroundColorSelectedAndHit}" />
</ObjectAnimationUsingKeyFrames>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="GridScaleTransform"
Storyboard.TargetProperty="(ScaleTransform.ScaleX)"
To="1.2" BeginTime="{TemplateBinding AnimationBeginTime}" Duration="00:00:00.300" AutoReverse="True">
<DoubleAnimation.EasingFunction>
<ExponentialEase EasingMode="EaseIn" />
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
<DoubleAnimation
Storyboard.TargetName="GridScaleTransform"
Storyboard.TargetProperty="(ScaleTransform.ScaleY)"
To="1.2" BeginTime="{TemplateBinding AnimationBeginTime}" Duration="00:00:00.300" AutoReverse="True">
<DoubleAnimation.EasingFunction>
<ExponentialEase EasingMode="EaseIn" />
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid.RenderTransform>
<ScaleTransform x:Name="GridScaleTransform" />
</Grid.RenderTransform>
<!-- ... -->
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I confirm that WinRT doesn't support Binding/TemplateBinding within a style. To workaround the issue I wrote code to manually update the BeginTime of the Storyboard. That way I had full control on when the Storyboard begins.
I would look at Interactivity. I've personally used the EventTrigger with GoToStateAction, which was enough for my purposes. From looking at MSDN it looks like you might be able to make use of TimerTrigger and GoToStateAction to create the effect you're looking for. TimerTrigger has dependency properties for setting the delay of the action to fire.
I've the following issue:
When I change orientation of my c#+xaml page from landscape to portrait or vice versa there is a half a second or so in which the user can see big blue parts on the screen, before the layout gets recalculated and re-rendered. These spots seem like leftovers from the previous orientation state.
I am looking for a way to hide or smoothen this extremely ragged and bumpy transition.
I tried adding an orientation change handler with a ProgressRing to cover it for 1 second, but it did not help - the handler executes after the blue spots.
Here is the code of my animation StoryBoard
<!-- The entire page respects the narrower 100-pixel margin convention for portrait -->
<VisualState x:Name="FullScreenPortrait">
<Storyboard>
<!-- Change the back button and the logo -->
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="backButton" Storyboard.TargetProperty="Style">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource PortraitBackButtonStyle}"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="headerLogoImage" Storyboard.TargetProperty="Width">
<DiscreteObjectKeyFrame KeyTime="0" Value="430"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="headerLogoImage" Storyboard.TargetProperty="Height">
<DiscreteObjectKeyFrame KeyTime="0" Value="49"/>
</ObjectAnimationUsingKeyFrames>
<!--Change section title and navButtons to be in two rows, by moving navButtons to the second row, first column-->
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="navButtons" Storyboard.TargetProperty="(Grid.Row)">
<DiscreteObjectKeyFrame KeyTime="0" Value="1"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="navButtons" Storyboard.TargetProperty="(Grid.Column)">
<DiscreteObjectKeyFrame KeyTime="0" Value="0"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="navButtons" Storyboard.TargetProperty="(Grid.ColumnSpan)">
<DiscreteObjectKeyFrame KeyTime="0" Value="2"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="navButtons" Storyboard.TargetProperty="Margin">
<DiscreteObjectKeyFrame KeyTime="0" Value="0,15,0,0"/>
</ObjectAnimationUsingKeyFrames>
<!--Change the grid-->
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemGridView" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemPortraitGridView" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Visible"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
Any help is appreciated, thanks!
EDIT:
I solved this by using the following method (and the help of my colleague):
/// <summary>
/// On Orientation change collapse all views, and make visible only the views for the particular new orientation
/// Also change the font size for portrait mode
/// </summary>
/// <param name="sender"></param>
private async void OnOrientationChanged(object sender)
{
headerGrid.Visibility = Visibility.Collapsed;
itemGridView.Visibility = Visibility.Collapsed;
itemPortraitGridView.Visibility = Visibility.Collapsed;
itemListView.Visibility = Visibility.Collapsed;
//Make the loading spinner temporarily visible and stop it in the StoryBoard animation for every orientation separately
LoadingView.Visibility = Visibility.Visible;
//Change the font size
if (ScreenHelper.IsInPortraitMode())
{
_viewModel.FontSizeMethod = _viewModel.GetPortraitFontSize;
}
else
{
//change font size method back
_viewModel.FontSizeMethod = _viewModel.GetLandscapeFontSize;
}
// Change visibility back to normal in case the xaml approach failed.
await Task.Delay(1000);
if (ScreenHelper.IsInPortraitMode())
itemPortraitGridView.Visibility = Windows.UI.Xaml.Visibility.Visible;
else if (ApplicationView.Value == ApplicationViewState.Snapped)
itemListView.Visibility = Windows.UI.Xaml.Visibility.Visible;
else
itemGridView.Visibility = Windows.UI.Xaml.Visibility.Visible;
headerGrid.Visibility = Visibility.Visible;
LoadingView.Visibility = Visibility.Collapsed;
}
I know you marked this complete but since I cant post a comment I have to ask here. Could you have just used easing to solve this?
I am building Windows Store Application (Winrt, Metro, Windows 8 app). I try give images in gridview animation: after image is loaded from web and opened it populates its cell.
I have storyboard for that:
<Storyboard x:Name="PopupImageStoryboard">
<DoubleAnimationUsingKeyFrames EnableDependentAnimation="True" Storyboard.TargetProperty="(FrameworkElement.Width)" Storyboard.TargetName="image">
<EasingDoubleKeyFrame KeyTime="0" Value="100"/>
<EasingDoubleKeyFrame KeyTime="0:0:0.1" Value="240"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames EnableDependentAnimation="True" Storyboard.TargetProperty="(FrameworkElement.Height)" Storyboard.TargetName="image">
<EasingDoubleKeyFrame KeyTime="0" Value="100"/>
<EasingDoubleKeyFrame KeyTime="0:0:0.1" Value="240"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
And I have such C# code for handling event that image loaded and opened:
private void Image_ImageOpened(object sender, RoutedEventArgs e)
{
Storyboard.SetTarget(PopupImageStoryboard, sender as Image);
PopupImageStoryboard.Begin();
}
It does not work, I can not change target and rerun same storyboard while it is running. But I want multiple images run this animation simultaneously. Can you recommend any solution for animation reuse ?
You should remove this from each of the child animations:
Storyboard.TargetName="image"
Then also I think a single Storyboard might not be possible to be used on two target elements at the same time, so the solution for that would be to put it in a DataTemplate, e.g.
<Page.Resources>
<DataTemplate
x:Name="myStoryboard">
<Storyboard ... />
</DataTemplate>
</Page.Resources>
Then in code you would say
var sb = (Storyboard)myStoryboard.LoadContent();
Storyboard.SetTarget(sb, sender as Image);
sb.Begin();
BTW - do not animate Width and Height properties. This is almost certainly not the best idea in your case. You should animate your RenderTransform properties instead, e.g. targeting ScaleTransform's ScaleX and ScaleY properties. If an animation is a dependent - it will cause layout updates in each frame which is very inefficient and will degrade your animation frame rate.
*EDIT
A better solution then would look like this:
<Page.Resources>
<DataTemplate
x:Name="myStoryboardTemplate">
<Storyboard>
<DoubleAnimation
Storyboard.TargetProperty="ScaleX"
From="0.5"
To="1.0"
Duration="0:0:0.4">
<DoubleAnimation.EasingFunction>
<BackEase
Amplitude="2"
EasingMode="EaseOut"/>
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
<DoubleAnimation
Storyboard.TargetProperty="ScaleY"
From="0.5"
To="1.0"
Duration="0:0:0.4">
<DoubleAnimation.EasingFunction>
<BackEase
Amplitude="2"
EasingMode="EaseOut" />
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
</DataTemplate>
</Page.Resources>
...
<Image
HorizontalAlignment="Center"
VerticalAlignment="Center"
Width="620"
Height="300"
Source="Assets/SplashScreen.png"
RenderTransformOrigin="0.5,0.5"
Stretch="Fill">
<Image.RenderTransform>
<ScaleTransform
x:Name="imageScaleTransform" />
</Image.RenderTransform>
</Image>
...
void MainPage_Loaded(object sender, RoutedEventArgs e)
{
var sb = (Storyboard)myStoryboardTemplate.LoadContent();
Storyboard.SetTarget(sb, imageScaleTransform);
sb.Begin();
}