Reputation: 51
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?
Upvotes: 2
Views: 2078
Reputation: 39006
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.
Upvotes: 14
Reputation: 4327
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.
Upvotes: 0