I am working on creating a reactive UI using a map and menu at the bottom. My map is using the builtin windows phone bing maps control with a user control below it.
My XAML looks like this:
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Controls:Map x:Name="map"/>
<Border Background="{StaticResource PhoneChromeBrush}"
Grid.Row="1">
<telerikPrimitives:RadTransitionControl Grid.Row="1">
<views:OverviewPage/>
</telerikPrimitives:RadTransitionControl>
</Border>
</Grid>
And my animation looks like this. I've drawn out the time for a longer period of time to show the issues.
DoubleAnimation animation = new DoubleAnimation() { Duration = TimeSpan.FromSeconds(10), To = 480, From = 0 };
Storyboard.SetTarget(animation, map);
Storyboard.SetTargetProperty(animation, new PropertyPath(HeightProperty));
Storyboard storyboard = new Storyboard();
storyboard.Children.Add(animation);
storyboard.Begin();
Now, if I swap out my Map control for something else, say a Border. Everything works as you would expect, the animation is smooth. The issue is my UI updates only every half second or so if I use the map. I'm getting this lag on a Lumia 1520, I can't imagine how bad it is on a lower end device.
I am looking for some way to improve the performance of this animation or some alternative. Has anyone tried this before or do you know a good solution?
This issue is that every time the map is moved, or resized there is a bunch of calculations that have to be done by the map. When you animate the map, the size of the map updates a bunch of times in a very short time, causing the map to recalculate everything a bunch of times. Map controls in general tend to be heavy weight controls due to all the data and calculations involved. Things like animating the map size should be avoided, especially on less powerful devices, such as mobile phones.
Related
I have a ScrollViewer with Image in it. I attached listener to MouseWheel event so I could zoom in/out the image. The code looks like this:
private void ImageMouseWheel(object sender, MouseWheelEventArgs e)
{
Matrix matrix = image.LayoutTransform.Value;
if (e.Delta > 0)
matrix.ScaleAt(1.5, 1.5, e.GetPosition(image).X, e.GetPosition(image).Y);
else
matrix.ScaleAt(1.0 / 1.5, 1.0 / 1.5, e.GetPosition(image).X, e.GetPosition(image).Y);
image.LayoutTransform = new MatrixTransform(matrix);
}
It pretty much works, but it has odd quirk -- when the image is so zoomed out that is at state "fit to parent" visually I cannot it zoom out more (visually), but the matrix is scaled still.
This is bad in sense that any computation after this point is wrong, and also it has strange effect in UI, say user starts from "fit to parent" zoom:
zoom out --> visually no change, internally matrix is zoomed out
zoom in --> surprise, visually no change, internally matrix is zoomed in
zoom in --> visually zoom in, matrix is also changed (OK)
Because of those problems I would like either to continue zooming out visually in order to keep both things (internal and visual) in sync.
How to achieve this?
Xaml:
<DockPanel>
<ScrollViewer Name="imageScrollViewer" Grid.Column="1" MouseWheel="ImageMouseWheel"
CanContentScroll="true" HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto">
<Image MouseWheel="ImageMouseWheel" Name="image" Source="{Binding Path=Bitmap}"
MouseMove="Image_MouseMove"></Image>
</ScrollViewer>
</DockPanel>
Update: Originally I asked how to continue scaling visually or stop internally, but I found out that the last step of zooming out, when the image hits the state "fit to parent" is already out of sync internally-visually. For example position of the mouse is not correctly decoded above such image when moving the mouse around. Only when I zoom in a little the positions are correct. So the only way I see is to scale it visually correctly.
Hmm, I am tempted to delete this embarrassing question, but on the other hand maybe someone will have the same problem in future.
The problem is image is zoomed out, but later the dock panel zoom it in back. The image does not know about it so the math goes astray. The solution is to add some container which does not do additional scaling on itself, like stack panel.
Thus the solution lies in xaml:
<DockPanel>
<StackPanel>
...
</StackPanel>
</DockPanel>
I have a wpf application with the crystal reports wpf control
<sap:CrystalReportsViewer
Name="crystalReportsViewer"
Width="auto"
Height="auto"
HorizontalAlignment="Center"
VerticalAlignment="Center"
ShowPrevPageButton="False"
ShowStatusbar="False"
ShowToolbar="False"
SourceUpdated="crystalReportsViewer_SourceUpdated"
TargetUpdated="crystalReportsViewer_TargetUpdated"
ToggleSidePanel="None" />
Now I want to rotate the report. As far as I know there is no built in function in the wpf control.
That is why my solution looks like this:
crystalReportsViewer.ViewerCore.LayoutTransform = new RotateTransform(90);
The problem here is that I can only perform one rotation, a second rotation fails. This means also that I can't rotate back to starting position... Any ideas?
I programm an universal Windows platform app with Visual Studio and I want to get a simple Blur effect on my main grid layout, but I don't know how to apply a GaussianBlurEffect object on my grid. I searched for a long time and I've readed the Microsoft documentation but I don't understand the Visual Layer part.
If anyone can give me a little explaination about visuals, it would be nice :)
Sorry if my English is bad, I'm french.
You will find a lot of good samples on the Windows UI DevLabs repository
The idea of Visuals is to provide a low level API (but not as low as DirectX) to handle a lot of GPU accelerated effects on the UI. It allows you to draw what you want or create some effects on the rendering.
Here is a very basic sample to show how to apply a blur effect on a Grid. (It works the same for any other UIElement).
This code is adding a layer over the one used by the XAML renderer to render the grid. This newest layer will apply an effect on top of the image rendered by the XAML renderer.
The XAML of the page:
<Page
x:Class="BlurSample.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:BlurSample"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" x:Name="MainGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Rectangle Fill="Red" />
<Rectangle Fill="Green" Grid.Column="1" />
<Rectangle Fill="Blue" Grid.Row="1" Grid.Column="1" />
<Rectangle Fill="Yellow" Grid.Row="1" />
</Grid>
And the code behind:
public sealed partial class MainPage : Page
{
private CompositionEffectBrush brush;
private Compositor compositor;
public MainPage()
{
this.InitializeComponent();
MainGrid.SizeChanged += OnMainGridSizeChanged;
compositor = ElementCompositionPreview.GetElementVisual(MainGrid).Compositor;
// we create the effect.
// Notice the Source parameter definition. Here we tell the effect that the source will come from another element/object
var blurEffect = new GaussianBlurEffect
{
Name = "Blur",
Source = new CompositionEffectSourceParameter("background"),
BlurAmount = 100f,
BorderMode = EffectBorderMode.Hard,
};
// we convert the effect to a brush that can be used to paint the visual layer
var blurEffectFactory = compositor.CreateEffectFactory(blurEffect);
brush = blurEffectFactory.CreateBrush();
// We create a special brush to get the image output of the previous layer.
// we are basically chaining the layers (xaml grid definition -> rendered bitmap of the grid -> blur effect -> screen)
var destinationBrush = compositor.CreateBackdropBrush();
brush.SetSourceParameter("background", destinationBrush);
// we create the visual sprite that will hold our generated bitmap (the blurred grid)
// Visual Sprite are "raw" elements so there is no automatic layouting. You have to specify the size yourself
var blurSprite = compositor.CreateSpriteVisual();
blurSprite.Size = new Vector2((float) MainGrid.ActualWidth, (float) MainGrid.ActualHeight);
blurSprite.Brush = brush;
// we add our sprite to the rendering pipeline
ElementCompositionPreview.SetElementChildVisual(MainGrid, blurSprite);
}
private void OnMainGridSizeChanged(object sender, SizeChangedEventArgs e)
{
SpriteVisual blurVisual = (SpriteVisual) ElementCompositionPreview.GetElementChildVisual(MainGrid);
if (blurVisual != null)
{
blurVisual.Size = e.NewSize.ToVector2();
}
}
}
Update: Animation
To animate the blur effect, you will have to do two things:
declare the property you want to animate
create the animation
To declare the property, you will have to change the blurEffectFactory creation. Notice the declaration of the Blur.BlurAmount property :
// we convert the effect to a blur that can be used to paint the visual layer
var blurEffectFactory = compositor.CreateEffectFactory(blurEffect, new[] { "Blur.BlurAmount" });
brush = blurEffectFactory.CreateBrush();
Once declared, you can use the Blur.BlurAmount property in any animation you want. Here, I'm declaring a continuous animation of 3 seconds which will blur/unblur the image:
var blurAnimation = compositor.CreateScalarKeyFrameAnimation();
blurAnimation.InsertKeyFrame(0.0f, 100.0f);
blurAnimation.InsertKeyFrame(0.5f, 0.0f);
blurAnimation.InsertKeyFrame(1.0f, 100.0f);
blurAnimation.Duration = TimeSpan.FromSeconds(3);
blurAnimation.IterationBehavior = AnimationIterationBehavior.Forever;
brush.StartAnimation("Blur.BlurAmount", blurAnimation);
I'm kinda new to WPF and just started looking into animations, and just for fun i started creating the old-school game Frogger (the one with a frog that has to cross a river/road while not getting hit by cars etc).
I made the animation for the log of wood moving across the screen in the river:
<Canvas>
<Rectangle Name="shape_WaterBackground" Fill="#1E90FF" Height="20" Width="260" Canvas.Top="240"/>
<Rectangle Name="shape_Lumber" Width="40" Canvas.Top="240" Fill="Brown" Height="16" Margin="0,2,0,0" />
<Image Name="Froggy" Height="20" Width="20" Canvas.Top="300" Canvas.Left="120" Source="Froggy.jpg" />
</Canvas>
And the code-behind for this particular animation:
DoubleAnimation Animate_Lumber_Movement = new DoubleAnimation(-40, 280, TimeSpan.Parse("0:0:7"));
Animate_Lumber_Movement.RepeatBehavior = RepeatBehavior.Forever;
shape_Lumber.BeginAnimation(Canvas.LeftProperty, Animate_Lumber_Movement);
Now my question is, how do I handle when the log reaches a certain point. The reason is (like you've probably already guessed) that I would like to know how to execute an action mid-way through the animation (a different point or time in the animation, and not a static) ofc without stopping the animation.
Its somewhat the same problem I have as described in here:How to determine when an animated sprite reaches a point?, but for C# WPF.
As an example in the code above, i want the frog to survive if it jumps towards the river and its Canvas.Leftproperty matches the animated logs, or otherwise drown.
Any suggestion is greatly appreciated (but bear in mind that I'm a newbie)
Sorry for the not-so-well-explained question :)
Thank you and best regards
Lodal
Since you're just animating the Canvas.Left property, you could always just set up a DispatcherTimer and read it (Canvas.GetLeft or Canvas.GetTop).
Although, maybe this is just me, but conceptually, WPF animations are more for short spurts, sort of like screen transitions and the like - not really for handling entire game animations. If that's your aim, you're probably just better off handling the animation yourself (set up a DispatcherTimer, and then on each tick you would update your game state).
Youtube has a nice control for votes like below screenshots,
Is there anything similar in WPF already or if I need to do it by myself, how to do it? I'm new to WPF, XAML and all, so I have this question.
I am not familiar that anything exactly like this already exists, there are Sparkline controls in some of the proprietary controls such as Telerik RadControls, but nothing exactly the same to the YouTube votes.
But it should be easy enough to create your own control using XAML.
Let's take a look, the control exists from 3 labels, a line that shows progress and two icons. To position this elements in a WPF user control, you can use a grid with three rows and two columns.
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
</Grid>
</Grid>
The labels are then placed into first and last rows using the XAML attributes Grid.Row and Grid.Column. The icons in last row can be placed using a StackPanel or just creating more columns, do as you wish. Everything here is easy.
The problem is the spark line, which I suggest you create two rectangles placed on top of one another. Both should go into second row, and span through all columns, you can achieve this using Grid.ColumnSpan attribute. First rectangle represents the background, so choose a light color for it. The second one represents the actual vote counter and should be colored red or green, depending on the votes.
Give all elements a name and you are finished with XAML (except minor corrections such as Margins and Horizontal or Vertical Alignments).
In code, create three properties for user control, all of type Integer (int). One for view count and two for up and down votes. Those properties can either be bound to labels in XAML, or you can update the values manually. Read more about Data binding here: http://wpftutorial.net/DataBindingOverview.html
To correctly place the rectangle that displays vote counter, you should just calculate the percentage of upvotes based on the properties, use the following code for help:
double percentage = UpvoteCount / (double)(UpvoteCount + DownvoteCount);
Note that I cast the sum to double, to keep percentage a floating point number (or you would always get a zero). From here all you need to do is to rescale the width of the rectangle to the appropriate percentage, considering that the background rectangle spans 100%. You can do this with the following code:
voteProgress.Width = percentage * voteBackground.ActualWidth;
In this case voteProgress and voteBackground are the names of your rectangles. Youtube also uses different colors, which you can change based on your calculated percentage:
if (percentage > 0.5)
voteProgress.Fill = new SolidColorBrush(Colors.Green);
else
voteProgress.Fill = new SolidColorBrush(Colors.Red);
The percentage MUST be calculated each time the size of the control changes (or number of votes), so look into SizeChanged event.
For more information and details, please read WPF tutorial, courtesy of Christian Moser.
http://wpftutorial.net