uwp Expression node fade in animation - c#

I am using the sample on Windows UI dev labs sample gallery. And I used ShyHeader example to put in my app, but I am not using exactly the same code but I actually edited the example according to own needs.
My question is how can I use expression node to fade in a specific XAML element in correspondence to the scroll viewer, I am able to fade out an element with the scroll viewer. but I am not being able to fade in an element from opacity 0 -> opacity 1.
here is my code.
<ScrollViewer x:Name="MyScrollViewer">
<Grid>
<local:MyAdaptiveView Margin="0,300,0,0"
x:Name="AllVideosGridView"/>
<Grid x:Name="Header" Height="300" VerticalAlignment="Top">
<FlipView x:Name="MainFlipView"
</FlipView>
<Grid Background="Blue" Height="150" VerticalAlignment="Bottom" Opacity="0.5" Name="FrontGrid">
</Grid>
</Grid>
</Grid>
</ScrollViewer>
page loaded method
the only important piece of code is only at the very end of this method, the last 4, 5 lines, you can see I am able to fade out element by doing 1- progresNode but my attempt to fade in another element ( frontVisual ) by doing 0+ progressNode doesn't work and frontVisual actually remains at 0 opacity even after I scroll.
private void ShyView_Loaded(object sender, RoutedEventArgs e)
{
// Get the PropertySet that contains the scroll values from MyScrollViewer
_scrollerPropertySet = ElementCompositionPreview.GetScrollViewerManipulationPropertySet(MyScrollViewer);
_compositor = _scrollerPropertySet.Compositor;
// Create a PropertySet that has values to be referenced in the ExpressionAnimations below
_props = _compositor.CreatePropertySet();
_props.InsertScalar("progress", 0);
_props.InsertScalar("clampSize", 150);
_props.InsertScalar("scaleFactor", 0.7f);
// Get references to our property sets for use with ExpressionNodes
var scrollingProperties = _scrollerPropertySet.GetSpecializedReference<ManipulationPropertySetReferenceNode>();
var props = _props.GetReference();
var progressNode = props.GetScalarProperty("progress");
var clampSizeNode = props.GetScalarProperty("clampSize");
var scaleFactorNode = props.GetScalarProperty("scaleFactor");
// Create a blur effect to be animated based on scroll position
var blurEffect = new GaussianBlurEffect()
{
Name = "blur",
BlurAmount = 0.0f,
BorderMode = EffectBorderMode.Hard,
Optimization = EffectOptimization.Balanced,
Source = new CompositionEffectSourceParameter("source")
};
var blurBrush = _compositor.CreateEffectFactory(
blurEffect,
new[] { "blur.BlurAmount" })
.CreateBrush();
blurBrush.SetSourceParameter("source", _compositor.CreateBackdropBrush());
// Create a Visual for applying the blur effect
_blurredBackgroundImageVisual = _compositor.CreateSpriteVisual();
_blurredBackgroundImageVisual.Brush = blurBrush;
_blurredBackgroundImageVisual.Size = new Vector2((float)Header.ActualWidth, (float)Header.ActualHeight);
// Insert the blur visual at the right point in the Visual Tree
ElementCompositionPreview.SetElementChildVisual(Header, _blurredBackgroundImageVisual);
// Create and start an ExpressionAnimation to track scroll progress over the desired distance
ExpressionNode progressAnimation = EF.Clamp(-scrollingProperties.Translation.Y / clampSizeNode, 0, 1);
_props.StartAnimation("progress", progressAnimation);
// Create and start an ExpressionAnimation to animate blur radius between 0 and 15 based on progress
ExpressionNode blurAnimation = EF.Lerp(0, 15, progressNode);
_blurredBackgroundImageVisual.Brush.Properties.StartAnimation("blur.BlurAmount", blurAnimation);
// Get the backing visual for the header so that its properties can be animated
Visual headerVisual = ElementCompositionPreview.GetElementVisual(Header);
// Create and start an ExpressionAnimation to clamp the header's offset to keep it onscreen
ExpressionNode headerTranslationAnimation = EF.Conditional(progressNode < 1, 0, -scrollingProperties.Translation.Y - clampSizeNode);
headerVisual.StartAnimation("Offset.Y", headerTranslationAnimation);
// Create and start an ExpressionAnimation to scale the header during overpan
ExpressionNode headerScaleAnimation = EF.Lerp(1, 1.25f, EF.Clamp(scrollingProperties.Translation.Y / 50, 0, 1));
headerVisual.StartAnimation("Scale.X", headerScaleAnimation);
headerVisual.StartAnimation("Scale.Y", headerScaleAnimation);
//Set the header's CenterPoint to ensure the overpan scale looks as desired
headerVisual.CenterPoint = new Vector3((float)(Header.ActualWidth / 2), (float)Header.ActualHeight, 0);
// Get the backing visual for the photo in the header so that its properties can be animated
Visual photoVisual = ElementCompositionPreview.GetElementVisual(MainFlipView);
// Create and start an ExpressionAnimation to opacity fade out the image behind the header
ExpressionNode imageOpacityAnimation = 1 - progressNode;
photoVisual.StartAnimation("opacity", imageOpacityAnimation);
// Get the front visual for the photo in the header so that its properties can be animated
Visual frontVisual = ElementCompositionPreview.GetElementVisual(FrontGrid);
// Create and start an ExpressionAnimation to opacity fade out the image behind the header
ExpressionNode imageOpacityAnimation2 = 0 + progressNode;
frontVisual.StartAnimation("opacity", imageOpacityAnimation2);
}
Note the behavior I actually want is that when I scroll down then FlipView should fade out and when I scroll up to the top it should fade in, which is working perfectly, but along with it I want FrontGrid to be exactly opposite, i.e: fade in on scroll down and fade out on scroll up.
Thanks in advance

Your expression looks OK.
Note the Opacity you are animating with Composition is the Opacity of Visual. However, the Opacity of 0.5 you are setting on FrontGrid XAML is from UIElement. Doing so will break the Composition opacity expression animation.
The fix is simple - Try getting the Visual of your FrontGrid right after InitializeComponent and set its Opacity to 0.5 there (i.e. frontVisual.Opacity = 0.5) instead of setting it in XAML.
You will see this kind of "weird" behaviors starting from the Anniversary Update, due to a XAML-Composition Interop Behavior change.
For a full explanation, please read this official document.
In short, XAML doesn't know if Composition has changed the Opacity, it still thinks it should be 0.5 as it was last set. So it will try to override and cause the animation to fail. This happens to a few more properties like Offset and Size too.
My advice is if you go Composition, try going Composition all the way. :)

Related

WPF Animation is Choppy

I am trying to use some animations to make my application feel good. But I could not help the choppy animation that no matter what I do, it is always end up stuttering.
Take a look:
DoubleAnimation anim = new DoubleAnimation()
{
//ht is height of DockPanel, I wanted to start from 200 less than Actual DockPanel Height
From = ht - 200,
To = ht,
Duration = TimeSpan.FromSeconds(1),
AccelerationRatio = 0.5,
DecelerationRatio = 0.5
};
x.BeginAnimation(HeightProperty, anim);
//x is the UserControl
Also, what I need to animate is a custom UserControl, which contains some text like 100 words and bunch of Images. I just want to make it grow in to the height of the current DockPanel as soon it is loaded.
What I saw by searching for the solution is this,
Timeline.SetDesiredFrameRate(anim, 10);
Even trying any value in there nothing really happens.
Framerate is like the frame rate of a film.
A low frame rate will give you a choppy film or a choppy animation.
Using a dockpanel is probably a bad idea for some content you are going to animate, because it will try and adjust things every time your height changes.
I suggest you go with a grid instead.
You should use a scaletransform. Partly because as you animate height you will find all the content of your usercontrol have their measures invalidated and they will want to start off the whole measure arrange cycle many times.
If you're thinking measure arrange? Then read up on how the wpf layout system works.
I would also advise you to use xaml rather than code.
Here's some code to think about:
private void StartAnimate_Click(object sender, RoutedEventArgs e)
{
var tran = testRectangle.RenderTransform = new ScaleTransform(1d, 1d)
{
CenterX = 0.5d,
CenterY = 0.5d
};
var anim = new DoubleAnimation
{
To = 1.0,
From=0,
Duration = TimeSpan.FromSeconds(0.5d),
DecelerationRatio = 0.5d,
FillBehavior = FillBehavior.Stop
};
tran.BeginAnimation(
ScaleTransform.ScaleYProperty,
anim,
HandoffBehavior.Compose);
}
I put a rectangle and my button in a dockpanel to prove this works.
<DockPanel>
<Rectangle Fill="Blue" Name="testRectangle"
Width="500"
/>
<Button Content="Animate"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Name="StartAnimate"
Click="StartAnimate_Click" />
</DockPanel>
A rectangle is pretty simple but it animates smoothly.

How to check if an UI Element has reached the top of the page in windows phone

There is a page which has a scroll viewer and some content which is dynamic in nature. In the middle of the page there is a grid. Whenever the user scrolls the page and grid reaches the top of the page i want a notifier. Basically i want to make the grid sticky on top whenever it reaches top of the page. Is there any way we can achieve this in Windows phone application. I dont want to calculate the offset because the content between top of the page and grid is dynamic.
This used to be tricky to do but thanks to the new Windows Composition API, it's now fairly simple.
Let's say I have a ScrollViewer named MainScroll which hosts a Grid called StickyGrid and I want to make the latter sticky once it hits the top.
There's the code with comments to explain what it does.
MainScroll.SizeChanged += (s, e) =>
{
// Let's first get the offset Y for the main ScrollViewer relatively to the sticky Grid.
var transform = ((UIElement)MainScroll.Content).TransformToVisual(StickyGrid);
var offsetY = (float)transform.TransformPoint(new Point(0, 0)).Y;
// Get Composition variables.
var scrollProperties = ElementCompositionPreview.GetScrollViewerManipulationPropertySet(MainScroll);
var stickyGridVisual = ElementCompositionPreview.GetElementVisual(StickyGrid);
var compositor = scrollProperties.Compositor;
// Basically, what the expression
// "ScrollingProperties.Translation.Y > OffsetY ? 0 : OffsetY - ScrollingProperties.Translation.Y"
// means is that -
// When ScrollingProperties.Translation.Y > OffsetY, it means the scroller has yet to scroll to the sticky Grid, so
// at this time we don't want to do anything, hence the return of 0;
// when the expression becomes false, we need to offset the the sticky Grid on Y Axis by adding a negative value
// of ScrollingProperties.Translation.Y. This means the result will forever be just OffsetY after hitting the top.
var scrollingAnimation = compositor.CreateExpressionAnimation("ScrollingProperties.Translation.Y > OffsetY ? 0 : OffsetY - ScrollingProperties.Translation.Y");
scrollingAnimation.SetReferenceParameter("ScrollingProperties", scrollProperties);
scrollingAnimation.SetScalarParameter("OffsetY", offsetY);
// Kick off the expression animation.
stickyGridVisual.StartAnimation("Offset.Y", scrollingAnimation);
};
Here is a working demo on GitHub.

Generate a circle where user taps(Clicks) in window phone8 App

I want to use the functionality in my window phone8 App that a circle should be generated where a user clicks on the phone screen. In android and IOS there is an event Touchpose. Is there any Equivalent method of touchpose in wp8.
or any other way to create a circle on screen where user taps(clicks). Thanks in advance.
Sure, you can. If your layout is based on a grid, then here's my example how you can do this.
XAML:
<Page /* all the default stuff */>
<Grid Tapped="Grid_Tapped" Background="Black">
// Your content
</Grid>
</Page>
C#:
private void Grid_Tapped(object sender, TappedRoutedEventArgs e)
{
Ellipse ellipse = new Ellipse()
{
Width = 64,
Height = 64,
Fill = new SolidColorBrush(Colors.Red),
Opacity = 0.2,
IsHitTestVisible = false, // This makes the circle transparent for touch, so you can tap things under it.
};
Grid.SetColumnSpan(ellipse, 99); // You need this if you have more columns than one.
Grid.SetRowSpan(ellipse, 99); // You need this if you have more rows than one.
Point position = e.GetPosition((Grid)sender);
ellipse.VerticalAlignment = VerticalAlignment.Top; // This will allow us to use top margin as Y coordinate.
ellipse.HorizontalAlignment = HorizontalAlignment.Left; // This will allow us to use left margin as X coordinate.
ellipse.Margin = new Thickness(position.X - ellipse.Width / 2, position.Y - ellipse.Height / 2, 0, 0); // Place the circle where user taps.
((Grid)sender).Children.Add(ellipse);
}
Note that the background of the main grid cannot be transparent, otherwise it won't fire Tapped event.
If you want, you can also animate the circle, change its size and color, make it disappear after some time...

TransformToVisual to element in a different visual tree within the same window

This is related to some custom drag and drop functionality I am implementing.
Everything was working very well until I threw a ContentPresenter into the mix.
I have a Window which contains a ContentPresenter.
The Content of this ContentPresenter is Bound different usercontrols which are popped in and out dynamically.
The problem I'm having is I need to perform a TransformToVisual on a control contained within the ContentPresenter relative to a control that lives outside in the main window where this COntentPresenter resides.
Summary:
Window -> Canvas called MyCanvas -> ContenPresenter (Content Presenter contains a ListView called MyListView)
I want to call MyListView.TransformToVisual(MyCanvas).
This is seemingly not allowed as I receive the error: "The specified Visual and this Visual do not share a common ancestor, so there is no valid transformation between the two Visuals."
Notes on snippet below:
1. _targetBoundingBoxes is a List of UIElements which should accept a drag
2. As I drag, I move a canvas around the screen (_canvasThatIsBeingDraggedAround).
3. As it moves, I am querying to see if the current MousePosition falls within any of the _dropTargets.
Code snippet that is failing:
_targetBoundingBoxes.Clear();
foreach (var item in _dropTargets)
{
GeneralTransform t = item.TransformToVisual(_canvasThatIsBeingDraggedAround);
Rect _dropBoundingBox = t.TransformBounds(new Rect(0, 0, item.RenderSize.Width, item.RenderSize.Height));
_targetBoundingBoxes.Add(item, _dropBoundingBox);
}
<Window>
<StackPanel>
<Button Margin="0,50,0,0" Height="50"/>
<ContentPresenter Name="HI" Content="{Binding Blah}"/>
</StackPanel>
</Window>
//Create a canvas which will be used as the dragged adorner. Canvas is used since you can set the Left and Top positions.
if (_topWindow.FindName("adornerLayer") == null)
{
//grab the existingContent
UIElement existingContent = (UIElement)_topWindow.Content;
//create a Grid wrapper around the entire window content so we can add the new canvas adornerLayer as a child in addition to the existing content
Grid nonLayoutCanvas = new Grid(); nonLayoutCanvas.VerticalAlignment = VerticalAlignment.Stretch; nonLayoutCanvas.HorizontalAlignment = HorizontalAlignment.Stretch;
//create the hidden Canvas that we will draw to and move around the screen
Canvas adornerCanvas = new Canvas(); adornerCanvas.Visibility = Visibility.Collapsed; adornerCanvas.Name = "adornerLayer";
adornerCanvas.Effect = new DropShadowEffect() { ShadowDepth = 5, BlurRadius = 5, Color = Colors.Silver };
//reset the content to the nonLayout Canvas
_topWindow.Content = nonLayoutCanvas;
//add the original content and the new canvas to the grid above
nonLayoutCanvas.Children.Add(existingContent);
nonLayoutCanvas.Children.Add(adornerCanvas);
_topWindow.RegisterName("adornerLayer", adornerCanvas);
}
_canvasThatIsBeingDraggedAround = (Canvas)_topWindow.FindName("adornerLayer");
Change TransformToVisual to TransformToAncestor.

C# WPF - Adorner ZIndex

I have a Grid with a Adorner to provide some drawn pattern. See img: http://imgur.com/D649W
My problem is that this Adorner(dots on the Grid) is layered on top of everything. The white square are draggable but now when the Adorner are on top, I can't drag. I would like the layer to be behind every component added to the Grid. Any suggestions on how I can set the ZIndex?
Thanks.
Code below:
MyAdorner ad = new MyAdorner(grid);
AdornerLayer adLayer = AdornerLayer.GetAdornerLayer(grid);
adLayer.Add(ad);
I push my Button and this is adding the MyAdorner to the grid. MyAdorner looks like this:
public MyAdorner(Grid adornedGrid)
: base(adornedGrid) {
Height = adornedGrid.Height;
Width = adornedGrid.Width;
brush = new VisualBrush();
brush.Stretch = Stretch.Fill;
brush.TileMode = TileMode.Tile;
brush.Viewport = new Rect(0, 0, SnapDistance, SnapDistance);
brush.ViewportUnits = BrushMappingMode.Absolute;
brush.Viewbox = new Rect(0, 0, SnapDistance, SnapDistance);
brush.ViewboxUnits = BrushMappingMode.Absolute;
ellipse = new Ellipse() { Fill = new SolidColorBrush(Colors.Blue), Width = 2, Height = 2 };
brush.Visual = ellipse;
}
protected override void OnRender(System.Windows.Media.DrawingContext drawingContext) {
Pen renderPen = new Pen(new SolidColorBrush(Colors.Black), 0);
drawingContext.DrawRectangle(brush, renderPen, new Rect(new Point(0, 0), AdornedElement.DesiredSize));
}
If your problem is that the adorner is covering the elements you want to manipulate so that they become un-draggable etc, set .IsHitTestVisible = False on the adorner.
You can also set the adorner's opacity to some semi-transparent value to see the background through it if that is desirable.
Is this what you're looking for?
Panel.SetZIndex(ad, 20)
Attached properties of the framework are usually asignable from static methods of the UIElement that holds it.
EDIT:
Possible alternative: - make your own Panel
Easy and dirty way to make sure that your wanted elements are ALWAYS on top:
Declare a static in a Util library:
public static int ZIndexCount;
Then when you want an element on top you simply do:
SetZIndex(_viewbox, Util.ZIndexCount++);
Of course, if your application runs 5 years without being interrupted the ZIndexCount will go back to 0...
It works like a charm in my applications.
I know this is quite old but I thought I try anyway:
You can add a new AdornerDecorator to you visual tree hierarchy to render the controls at the right level. By default the root of the tree provides the AdornerDecorator but you can add as many as you want and your the components you add will be rendered in them. For more information - see here
<Grid>
<AdornerDecorator>
...your Adorners render here
</AdornerDecorator>
</Grid>
https://wangmo.wordpress.com/2008/10/19/relations-between-adorner-adornerlayer-and-adornerdecorator/

Categories

Resources