I'm trying to make a custom hamburger menu for my app. I have the layout all setup, but, for some reason, my DoubleAnimation isn't working as I expect. I'm trying to accomplish something similar to Cortana's hamburger menu on Windows 10. I'm creating my Storyboard and DoubleAnimation in my code (I still have the same problem even if my animation is created in XAML). Here's what I have:
menuButton.Click += (s, e) => {
if (menuIsOpen) {
Animate(
menuPanel,
"Width",
ActualWidth, //Page.ActualWidth
50,
0.1,
new Duration(new TimeSpan(TimeSpan.TicksPerSecond)));
}
else {
Animate(
menuPanel,
"Width",
50,
ActualWidth, //Page.ActualWidth
0.1,
new Duration(new TimeSpan(TimeSpan.TicksPerSecond)));
}
menuIsOpen = !menuIsOpen;
};
This is the signature for Animate():
private void Animate(DependencyObject item, string property, double from, double to, double? by, Duration duration) {
var storyboard = new Storyboard();
var animation = new DoubleAnimation();
animation.From = from;
animation.To = to;
animation.Duration = duration;
animation.EasingFunction = new SineEase() { EasingMode = EasingMode.EaseInOut };
animation.By = by;
Storyboard.SetTarget(animation, item);
Storyboard.SetTargetProperty(animation, property);
storyboard.Children.Add(animation);
storyboard.Begin();
}
I used a little Debug.WriteLine() to see if the Storyboard is actually running, which it is. The menuPanel's width does not change at all. I've used a similar approach with some of my other projects and it works fine there.
EDIT:
I thought I'd add a little bit more detail.
My app is targeting Windows 10
and I'm able to change the width of menuPanel freely if I don't use Storyboards and DoubleAnimations.
I found this answer on a similar question, and my animation works just fine now.
I figured it out myself. All I had to do was to Enable Dependent Animation (EnableDependentAnimation) on the DoubleAnimation as this animation affects the layout. And then it worked perfectly.
Related
I have an application that needs to give status updates, but they're pretty easy to miss. I want to very lightly and very briefly pulse the status bar when messages are posted. Almost every guide I've seen so far only lists how to set it up on hover or click using XAML instead of actual C#. I want to call a function to pulse the bar when I'm updating status messages in C#:
This is what I have and it doesn't work at all.
private void PulseStatus()
{
ColorAnimation animation;
animation = new ColorAnimation();
animation.To = Color.FromRgb(111, 80, 80);
animation.Duration = new Duration(TimeSpan.FromSeconds(.4));
animation.AutoReverse =true;
StatusArea.Background.BeginAnimation(WrapPanel.BackgroundProperty,animation);
}
I tried it and i found some mistakes. It has nothing todo with XAML as everything that can be written in XAML can be written in C# as well.
However:
First you want to animate the Color property of the SolidBrush object. And second, at least in my try i could not animate the pre set background brush of the wrap panel (it said it was "sealed"). You might also want to set the RepeatBehavior to Forever, to keep the animation running.
ColorAnimation animation;
animation = new ColorAnimation();
animation.To = Color.FromRgb(111, 80, 80);
animation.Duration = new Duration(TimeSpan.FromSeconds(.4));
animation.AutoReverse = true;
animation.RepeatBehavior = RepeatBehavior.Forever;
var clonedBackgroundBrush = wrap.Background.Clone();
wrap.Background = clonedBackgroundBrush;
clonedBackgroundBrush.BeginAnimation(SolidColorBrush.ColorProperty, animation);
In my UWP App I've got a grid with 2 columns. The App is adaptive and on the mobile I only want to show one column at a time. Is there a way to use animations to reduce the width from column 1 and expand the width from column 2 and the other way round?
Animating size and layout has always been tricky in XAML frameworks. Why? Not because you cannot animate a Width, you can, but the performance usually sucks as a change to the Width/Height automatically triggers layout updates which then do a lot of re-calculating, re-measuring and re-arranging stuff on the UI thread that hurts the performance.
But there's always some workarounds you can do. With Windows Composition API, it's now a lot easier to animate layout changes while maintaining 60 frames per second fresh rate, all thanks to the new API such as ImplicitAnimations, SetImplicitHideAnimation & SetImplicitShowAnimation.
ImplicitAnimations basically allows you to monitor property changes like Opacity, Offset, Size, etc and whenever they are updated, the old value will be animated to the new value smoothly; where SetImplicitHideAnimation & SetImplicitShowAnimation will simply animate when the Visibility of an element is changed. So instead of disappearing instantly, one element can scale down and fade out.
Note you will need to provide your desired animations for the APIs to know how to animate. To make your life a bit easier, I have created some helper methods (see link at the bottom) that encapsulates some key animations that you generally need.
To find out exactly what they do, take a look at the gif below
I am re-positioning, hiding and showing elements in different adaptive visual states, no animation is written in XAML, but with the following code, the Composition API simply takes care of animating all these changes implicitly.
var compositor = this.Visual().Compositor;
// Create background visuals.
var leftBackgroundVisual = compositor.CreateSpriteVisual();
leftBackgroundVisual.Brush = compositor.CreateColorBrush(Colors.Crimson);
LeftGridBackgroundVisualWrapper.SetChildVisual(leftBackgroundVisual);
var middleBackgroundVisual = compositor.CreateSpriteVisual();
middleBackgroundVisual.Brush = compositor.CreateColorBrush(Colors.Gold);
MiddleGridBackgroundVisualWrapper.SetChildVisual(middleBackgroundVisual);
var rightBackgroundVisual = compositor.CreateSpriteVisual();
rightBackgroundVisual.Brush = compositor.CreateColorBrush(Colors.DarkOrchid);
RightGridBackgroundVisualWrapper.SetChildVisual(rightBackgroundVisual);
// Sync background visual dimensions.
LeftGridBackgroundVisualWrapper.SizeChanged += (s, e) => leftBackgroundVisual.Size = e.NewSize.ToVector2();
MiddleGridBackgroundVisualWrapper.SizeChanged += (s, e) => middleBackgroundVisual.Size = e.NewSize.ToVector2();
RightGridBackgroundVisualWrapper.SizeChanged += (s, e) => rightBackgroundVisual.Size = e.NewSize.ToVector2();
// Enable implilcit Offset and Size animations.
LeftText.EnableImplicitAnimation(VisualPropertyType.Offset, 400);
MiddleText.EnableImplicitAnimation(VisualPropertyType.Offset, 400);
RightText.EnableImplicitAnimation(VisualPropertyType.Offset, 400);
LeftGrid.EnableImplicitAnimation(VisualPropertyType.Offset, 400);
MiddleGrid.EnableImplicitAnimation(VisualPropertyType.Offset, 400);
RightGrid.EnableImplicitAnimation(VisualPropertyType.Offset, 400);
leftBackgroundVisual.EnableImplicitAnimation(VisualPropertyType.Size, 400);
middleBackgroundVisual.EnableImplicitAnimation(VisualPropertyType.Size, 400);
rightBackgroundVisual.EnableImplicitAnimation(VisualPropertyType.Size, 400);
// Enable implicit Visible/Collapsed animations.
LeftGrid.EnableFluidVisibilityAnimation(showFromScale: 0.6f, hideToScale: 0.8f, showDuration: 400, hideDuration: 250);
MiddleGrid.EnableFluidVisibilityAnimation(showFromScale: 0.6f, hideToScale: 0.8f, showDelay: 200, showDuration: 400, hideDuration: 250);
RightGrid.EnableFluidVisibilityAnimation(showFromScale: 0.6f, hideToScale: 0.8f, showDelay: 400, showDuration: 400, hideDuration: 250);
There's quite a lot of code so I am not posting everything here. But feel free to check it out from this link.
You can use bind to do it. And you should make two property in Page that code is below.
public static readonly DependencyProperty RcProperty = DependencyProperty.Register(
"Rc", typeof(double), typeof(MainPage), new PropertyMetadata(100d));
public double Rc
{
get { return (double) GetValue(RcProperty); }
set { SetValue(RcProperty, value); }
}
public static readonly DependencyProperty LcProperty = DependencyProperty.Register(
"Lc", typeof(double), typeof(MainPage), new PropertyMetadata(500d));
public double Lc
{
get { return (double) GetValue(LcProperty); }
set { SetValue(LcProperty, value); }
}
But we cant bind double to GridLength that we should add a convert.
public class DoubletoGridConvert : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
var n = (double) value;
return new GridLength(n);
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
After we wrote it, we can make the Page like below.
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="{x:Bind Rc,Mode=OneWay,Converter={StaticResource double}}"/>
<RowDefinition Height="{x:Bind Lc,Mode=OneWay,Converter={StaticResource double}}"/>
</Grid.RowDefinitions>
<Grid Background="#FF565656"></Grid>
<Grid Grid.Row="1" Background="#FFa2a2a2"></Grid>
</Grid>
<Button Margin="47,662,0,10" Content="set" Click="Button_OnClick"></Button>
We do the animation when button clicked.
private void Button_OnClick(object sender, RoutedEventArgs e)
{
this.Name = nameof(MainPage);
var storyboard = new Storyboard();
var animation = new DoubleAnimation();
Storyboard.SetTargetName(animation, nameof(MainPage));
Storyboard.SetTarget(animation, this);
Storyboard.SetTargetProperty(animation,"Rc");
animation.EnableDependentAnimation = true;
animation.From = 100;
animation.To = 200;
animation.Duration = new Duration(TimeSpan.FromMilliseconds(500));
storyboard.Children.Add(animation);
storyboard.Begin();
storyboard = new Storyboard();
animation = new DoubleAnimation();
Storyboard.SetTarget(animation, this);
Storyboard.SetTargetName(animation,nameof(MainPage));
Storyboard.SetTargetProperty(animation, nameof(Lc));
animation.From = 500;
animation.To = 150;
animation.Duration = new Duration(TimeSpan.FromMilliseconds(500));
animation.EnableDependentAnimation = true;
storyboard.Children.Add(animation);
storyboard.Begin();
}
I think it can help you.
I need to make a border slide from the bottom of the screen into view, however i'm having issues getting the ActualHeight of my border control. Because this is animation code i'm putting it in the code-behind as it's the Views responsibility.
My Border has got it's Loaded event tied to this:
private void NotifcationWindow_Loaded(object sender, RoutedEventArgs e)
{
SlideFromBottom(sender);
}
The Sender is the Border object, so the SlideFromBottom method SHOULD be able to use this object and get it's height as it has already been rendered.
public void SlideFromBottom(object sender)
{
//The Notification container
Border notification = (sender as Border);
//The screen size
var workingArea = SystemParameters.WorkArea;
Storyboard sb = new Storyboard();
var animation = new DoubleAnimation()
{
BeginTime = TimeSpan.FromSeconds(0),
Duration = TimeSpan.FromSeconds(5),
// Get the height and turn it to a negative value, and add screen height.
From = (notification.ActualHeight * -1),
//Slide the border into view
To = notification.ActualHeight
};
Storyboard.SetTarget(animation, notification);
Storyboard.SetTargetProperty(animation, new PropertyPath("(Margin.Bottom)"));
sb.Begin();
}
I'm not receiving any errors, but the animation isn't playing, have I got something wrong? The Border is Vertically Aligned to the bottom, so the negative margin should take it off the screen.
The reason why you're not seeing anything is that you haven't added the animation to the storyboard:
sb.Children.Add(animation);
Then there are some more problems. Such that a part of the margin cannot be animated separately. You would need a ThicknessAnimation.
But there is an easier solution. Use the RenderTransform. If you give your border the following render transform:
<Border>
<Border.RenderTransform>
<TranslateTransform/>
</Border.RenderTransform>
</Border>
, then you can animate it as follows:
// ...
var animation = new DoubleAnimation()
{
BeginTime = TimeSpan.FromSeconds(0),
Duration = TimeSpan.FromSeconds(5),
From = notification.ActualHeight,
To = 0
};
Storyboard.SetTarget(animation, notification);
Storyboard.SetTargetProperty(animation, new PropertyPath("RenderTransform.Y"));
sb.Children.Add(animation);
// ...
I have a WPF application that loads Pages in a templated NavigationWindow. I'd like to implement a slide transition when a new page is loaded and since the window can be resized the target values for the transform need to be determined programatically as far as I am aware.
I tried the following in the NavigationWindow code-behind but it has no effect when it fires. The PageContentContainerTransform is also being correctly located as determined from the debugger.
public void DoTransition()
{
double targetX = this.ActualWidth;
this.TransitionStoryboard.Stop();
this.TransitionStoryboard.Children.Clear();
IEasingFunction easing = new QuadraticEase() { EasingMode = EasingMode.EaseOut };
DoubleAnimation translateXAnim = new DoubleAnimation() {
To = targetX,
Duration = TimeSpan.FromMilliseconds(250),
EasingFunction = easing,
};
DependencyObject d = this.Template.FindName("pageContentContainerTransform", this) as DependencyObject;
Storyboard.SetTarget(translateXAnim, d);
Storyboard.SetTargetProperty(translateXAnim, new PropertyPath(TranslateTransform.XProperty));
this.TransitionStoryboard.Children.Add(translateXAnim);
this.TransitionStoryboard.Begin();
}
The Template is a ControlTemplate containing the following bit of XAML,
...
<ContentPresenter
Grid.Row="1"
x:Name="pageContentContainer"
MaxHeight="{StaticResource ContentWindowMaxHeight}"
MaxWidth="{StaticResource ContentWindowMaxWidth}"
RenderTransformOrigin="0.5,0.5">
<ContentPresenter.RenderTransform>
<TranslateTransform x:Name="pageContentContainerTransform" X="0" Y="0" />
</ContentPresenter.RenderTransform>
</ContentPresenter>
...
Why is there no effect?
Update
The animation works if you animate the element directly without wrapping in a Storyboard object. E.g.
public void DoTransition()
{
double targetX = this.ActualWidth;
this.TransitionStoryboard.Stop();
this.TransitionStoryboard.Children.Clear();
IEasingFunction easing = new QuadraticEase() { EasingMode = EasingMode.EaseOut };
DoubleAnimation translateXAnim = new DoubleAnimation() {
To = targetX,
Duration = TimeSpan.FromMilliseconds(250),
EasingFunction = easing,
};
TranslateTransform t = this.Template.FindName("pageContentContainerTransform", this) as TranslateTransform;
t.BeginAnimation(TranslateTransform.XProperty, translateXAnim);
}
However presumably you miss out on some nice control elements for the animations that the Storyboard object provides e.g. managing the animations (Stop, Start etc.). There appears to be possible arguments to .Begin() on the storyboard object that are pertinent to use within a Template, however calling with .Begin(this, this.Template) also does not do anything.
In the end a combination of factors got it to work. First, use Storyboard.SetTargetName rather than Storyboard.SetTarget. Secondly pass in the template context to the Begin() method. E.g.
public void DoTransition()
{
double targetX = this.ActualWidth;
this.TransitionStoryboard.Stop();
this.TransitionStoryboard.Children.Clear();
IEasingFunction easing = new QuadraticEase() { EasingMode = EasingMode.EaseOut };
DoubleAnimation translateXAnim = new DoubleAnimation() {
To = targetX,
Duration = TimeSpan.FromMilliseconds(250),
EasingFunction = easing,
};
// 1. Refer to the element by Name
Storyboard.SetTargetName(translateXAnim, "pageContentContainerTransform");
Storyboard.SetTargetProperty(translateXAnim, new PropertyPath(TranslateTransform.XProperty));
this.TransitionStoryboard.Children.Add(translateXAnim);
// 2. Pass in the template context here
this.TransitionStoryboard.Begin(this, this.Template);
}
I'm not clear why the SetTargetProperty does not work when you consider that FindName correctly identified the element within the template, but in any case the above methods works.
I have several boxes from type Windows.UI.Xaml.Controls.Control with different sizes. I wanna transform a few of them vertically. Like shown in the picture.
I'm struggling doing this.I'm sure that should not be very difficult but I don't get it...
Btw. I wanna do that in code behind not in XAML.
Many thanks for your help.
Cheers
Daniel
edit:
DoubleAnimation scaleAnimation = new DoubleAnimation();
scaleAnimation.From = startHeight;
scaleAnimation.To = this.ClientHeight * Percentage;
scaleAnimation.Duration = TimeSpan.FromMilliseconds(500);
scaleAnimation.EasingFunction = new QuarticEase() { EasingMode = EasingMode.EaseOut };
Storyboard storyScaleX = new Storyboard();
storyScaleX.Children.Add(scaleAnimation);
Storyboard.SetTarget(storyScaleX, slice);
scaleAnimation.EnableDependentAnimation = true;
Storyboard.SetTargetProperty(storyScaleX, "Height");
You can apply a TranslateTransform to the LayoutTransform or RenderTransform of the element (depending on what you need). e.g.
element.LayoutTransform = new TranslateTransform(0, 100)
If the effect you require depends on the height of the element, use the element's ActualHeight as the value to translate by.