WPF: Animation is not smooth - wpf

I am animating a TextBlock. In 60 seconds, it increases FontSize from 8pt to 200pt. Everything is working fine, except that my animation is moving up and down a bit as the text grows. Why is this happening and is it possible to avoid this?
I have a very simple XAML file:
<Window x:Class="Timer.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="800"
Height="500"
Title="MainWindow"
Loaded="Window_Loaded">
<Grid>
<TextBlock
Name="TimerTextBlock"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="00h : 00m : 00.000s" />
</Grid>
</Window>
And equally simple code-behind:
public partial class MainWindow : Window
{
private const string timerFormat = "{0:hh'h : 'mm'm : 'ss'.'fff's'}";
private DispatcherTimer dispatcherTimer;
private DateTime targetTime;
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
targetTime = DateTime.Now.AddSeconds(60);
double totalTime = targetTime.Subtract(DateTime.Now).TotalMilliseconds;
DoubleAnimation animation = new DoubleAnimation();
animation.From = TimerTextBlock.FontSize;
animation.To = 200;
animation.Duration = new Duration(targetTime.Subtract(DateTime.Now));
TimerTextBlock.BeginAnimation(TextBlock.FontSizeProperty, animation);
dispatcherTimer = new DispatcherTimer();
dispatcherTimer.Interval = TimeSpan.FromMilliseconds(1);
dispatcherTimer.Tick += new EventHandler(dispatcherTimer_Tick);
dispatcherTimer.Start();
}
private void dispatcherTimer_Tick(object sender, EventArgs e)
{
if (DateTime.Compare(targetTime, DateTime.Now) > 0)
{
TimerTextBlock.Text =
string.Format(timerFormat, targetTime.Subtract(DateTime.Now));
}
}
}
Thank you for all the clarifications.

Your vertical jumping problem is due to font rendering rounding. Specifically, WPF will avoid subpixel font height in order to enable font smoothing. One way to avoid this is to convert your text into a path geometry and then use a scale transform to animate it.
Here is an alternate version of your example without the jumping. The new XAML is:
<Grid>
<Path Name="Path" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
and the new code when you load the window:
SetText("");
var transform = new ScaleTransform(1, 1);
Path.LayoutTransform = transform;
var animationX = new DoubleAnimation(1, 10, new Duration(TimeSpan.FromSeconds(60)));
transform.BeginAnimation(ScaleTransform.ScaleXProperty, animationX);
var animationY = new DoubleAnimation(1, 10, new Duration(TimeSpan.FromSeconds(60)));
transform.BeginAnimation(ScaleTransform.ScaleYProperty, animationY);
and a new method to set the text that is anmiated:
private void SetText(string text)
{
var formatted = new FormattedText(text, CultureInfo.CurrentCulture, FlowDirection.LeftToRight, new Typeface("Lucida Console"), 12, Brushes.Black);
Path.Data = formatted.BuildGeometry(new Point(0, 0));
Path.Fill = Brushes.Black;
}
and you have call SetText from your timer event handler.
Note that to avoid horizontal jumpiness, you have to use a fixed-length text string and a constant-width font.

Related

Creating Thumbnail Preview like Powerpoint thumnails in wpf

I have a wpf application with two panes similar to powerpoint application:
left pane which shows list of all the panels in listbox
right pane which shows the selected panel
In the listbox I want to display panel as thumbnail and update the thumbnail as an when new controls are added to panel in right pane.
Just like powerpoint application thumbnail behaviour.
By using RenderTargetBitmap and PngBitmapEncoder we can capture a region of window.
and by using the PngBitmapEncoder frame Property assigned it to Image Source.
Lets start with Xaml
I divided the window by two half and left and right panel. Same in PowerPoint with less style. In order to demonstrate I have implemented to add TextBox on the right panel and the preview will be displayed on the left panel thumbnail.
<Grid Background="Aqua" x:Name="gridg">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<ListBox HorizontalAlignment="Left" Height="372" Margin="10,38,0,0" VerticalAlignment="Top" Width="306" Grid.Column="0" x:Name="Listtems" SelectionChanged="Listtems_SelectionChanged" />
<Button Content="+ TextBox" HorizontalAlignment="Left" Margin="142,10,0,0" VerticalAlignment="Top" Width="174" Click="Button_Click" Grid.Column="0"/>
<StackPanel x:Name="stackPanel" Background="Wheat" Grid.ColumnSpan="2" Margin="321,0,0,0" />
</Grid>
As soon as you click on the left panel item, the corresponding the control will be displayed on the right panel with the data.
In order to keep track of the items in the ListBox, I have used Dictionary with ItemIndex and to it's corresponding item's index used control.
Window's Code Behind
/// <summary>
/// Interaction logic for Window6.xaml
/// </summary>
public partial class Window6 : Window
{
Dictionary<int, Control> _dictionaryControls = new Dictionary<int, Control>();
DispatcherTimer dispatcherTimer = new DispatcherTimer();
public Window6()
{
InitializeComponent();
dispatcherTimer.Interval = new TimeSpan(0, 0, 1);
dispatcherTimer.Tick += new EventHandler(dispatcherTimer_Tick);
dispatcherTimer.Start();
}
private void BmpImage()
{
RenderTargetBitmap renderTargetBitmap =
new RenderTargetBitmap(800, 450, 96, 96, PixelFormats.Pbgra32);
renderTargetBitmap.Render(stackPanel);
PngBitmapEncoder pngImage = new PngBitmapEncoder();
pngImage.Frames.Add(BitmapFrame.Create(renderTargetBitmap));
Image img = new Image();
img.Source = pngImage.Frames[0];
img.Height = 148;
img.Width = 222;
Listtems.Items.Add(img);
Listtems.SelectedIndex = Listtems.Items.Count - 1;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
stackPanel.Children.Clear();
int item = Listtems.Items.Count;
TextBox txtControl = new TextBox();
txtControl.FontSize = 100;
txtControl.Height = 122;
txtControl.TextWrapping = TextWrapping.Wrap;
_dictionaryControls.Add(item, txtControl);
stackPanel.Children.Add(txtControl);
stackPanel.UpdateLayout();
BmpImage();
}
private void Listtems_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
UpdateThumbNail();
}
private void UpdateThumbNail()
{
int indexbackup = -1;
Listtems.SelectionChanged -= Listtems_SelectionChanged;
Control control;
_dictionaryControls.TryGetValue(Listtems.SelectedIndex, out control);
if (control == null)
{
Listtems.SelectionChanged += Listtems_SelectionChanged;
return;
}
indexbackup = Listtems.SelectedIndex;
stackPanel.Children.Clear();
stackPanel.Children.Add(control);
stackPanel.UpdateLayout();
RenderTargetBitmap renderTargetBitmap =
new RenderTargetBitmap(800, 450, 96, 96, PixelFormats.Pbgra32);
renderTargetBitmap.Render(stackPanel);
PngBitmapEncoder pngImage = new PngBitmapEncoder();
pngImage.Frames.Add(BitmapFrame.Create(renderTargetBitmap));
Image img = new Image();
img.Source = pngImage.Frames[0];
img.Height = 148;
img.Width = 222;
Listtems.Items.Insert(Listtems.SelectedIndex, img);
Listtems.Items.RemoveAt(Listtems.SelectedIndex);
Listtems.SelectedIndex = indexbackup;
Listtems.SelectionChanged += Listtems_SelectionChanged;
}
private void dispatcherTimer_Tick(object sender, EventArgs e)
{
UpdateThumbNail();
}
}
BmpImage() : - I used to capture or in other words the print screen of the StackPanel control.
Button_Click Event :- Is used to create a new Item in ListBox adding Image with the current print screen of the TextBox Control in StackPanel. It also adds control in _dictionaryControls variable.
Listtems_SelectionChanged Event:- Clears the StackPanel and then take the TextBox Control from _dictionaryControls based on the SelectedIndex of ListBox and place it in the StackPanel by taking current snapshot of the StackPanel.
For Demo Purpose, I have done it only for TextBox Control, but you can do it for any other control with a little tweaking.
UpdateThumbNail created a method responsible to update the image in Listbox based on the ListBoxItem.
dispatcherTimer_Tick : - Event is responsible to call the UpdateThumbNail() Method for every second.

Why the trigger doesn't work after GridSplitter is dragged

Due to project privacy protection, I can't paste the product code here. So I made a simple example to show the problem.
I have a window like this:
When I click the Right button, I want the Right Column hide and the Left Column Stretch to full window:
Here is my Xaml:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition MinWidth="0"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition x:Name="rightCol" MinWidth="0"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0">
<Button Height="100" Content="Left" />
</StackPanel>
<GridSplitter Grid.Column="1" Width="10" VerticalAlignment="Stretch" HorizontalAlignment="Center"/>
<StackPanel x:Name="right" Grid.Column="2" >
<Button Height="100" Content="Right" Click="Button_Click" />
</StackPanel>
</Grid>
</Window>
And here is my code-behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
var b = new Binding("Visibility")
{
Source = this.right
};
var style = new Style(typeof(ColumnDefinition))
{
Setters =
{
new Setter(ColumnDefinition.WidthProperty, new GridLength(300)),
new Setter(ColumnDefinition.MinWidthProperty, 0.0),
}
};
style.Triggers.Add(new DataTrigger()
{
Binding = b,
Value = System.Windows.Visibility.Collapsed,
Setters =
{
new Setter(ColumnDefinition.WidthProperty, new GridLength(0)),
new Setter(ColumnDefinition.MaxWidthProperty, double.PositiveInfinity),
new Setter(ColumnDefinition.MinWidthProperty, 0.0),
}
});
this.rightCol.Style = style;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
this.right.Visibility = System.Windows.Visibility.Collapsed;
}
}
When I run it and Click the Right button, it works fine. But if I drag the GridSplitter before I Click the Right button, the trigger doesn't work:
Why this happened?
Note:
If I use below code, it can work.
private void Button_Click(object sender, RoutedEventArgs e)
{
this.right.Visibility = System.Windows.Visibility.Collapsed;
this.rightCol.Width = new GridLength(0);
}
But is there any way to use trigger?
I got my answer from this link:
GridSplitter overrides ColumnDefinition's style trigger?
So below code works fine:
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
var b = new Binding("Visibility")
{
Source = this.rightPane,
Mode = BindingMode.OneWay
};
var style = new Style(typeof(ColumnDefinition))
{
Setters =
{
new Setter(ColumnDefinition.WidthProperty, new GridLength(300)),
new Setter(ColumnDefinition.MinWidthProperty, 0.0),
}
};
/*
style.Triggers.Add(new DataTrigger()
{
Binding = b,
Value = System.Windows.Visibility.Collapsed,
Setters =
{
new Setter(ColumnDefinition.WidthProperty, new GridLength(0)),
new Setter(ColumnDefinition.MaxWidthProperty, double.PositiveInfinity),
new Setter(ColumnDefinition.MinWidthProperty, 0.0),
}
});*/
Storyboard sb = new Storyboard();
ObjectAnimationUsingKeyFrames oaf = new ObjectAnimationUsingKeyFrames();
DiscreteObjectKeyFrame disobj = new DiscreteObjectKeyFrame();
disobj.KeyTime = TimeSpan.FromMilliseconds(0);
disobj.Value = GridLength.Auto;
oaf.KeyFrames.Add(disobj);
sb.Children.Add(oaf);
Storyboard.SetTargetProperty(oaf, new PropertyPath(ColumnDefinition.WidthProperty));
var actionBegin = new BeginStoryboard();
actionBegin.Storyboard = sb;
actionBegin.Name="BeginStoryboard1";
var actionEnd = new RemoveStoryboard();
actionEnd.BeginStoryboardName = "BeginStoryboard1";
var trigger = new DataTrigger()
{
Binding = b,
Value = System.Windows.Visibility.Collapsed
};
trigger.EnterActions.Add(actionBegin);
trigger.ExitActions.Add(actionEnd);
style.Triggers.Add(trigger);
style.RegisterName(actionBegin.Name, actionBegin);
this.rightCol.Style = style;
}

WPF. if pop up window appear, main window brightness decrease //code-behind

I need that if my pop up window appear (after click) , the main window brightness has to decrease, maybe someone know how to do it?
Example:
EDIT: I create canvas, but do not know how to use it, brightness need decrease then pop up appear.
code:
private void sample_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
string path1 = System.AppDomain.CurrentDomain.BaseDirectory + "../../loader_bg.png";
string path2 = System.AppDomain.CurrentDomain.BaseDirectory + "../../loader.gif";
ImageBrush myBrush = new ImageBrush();
Image image = new Image();
image.Source = new BitmapImage(
new Uri(path1));
myBrush.ImageSource = image.Source;
Image ima = new Image();
MediaElement gif = new MediaElement();
ima.Source = new BitmapImage(new Uri(path1));
gif.Source=new Uri(path2);
gif.Height = 72;
gif.Width = 72;
var pop = new Popup
{
IsOpen = true,
StaysOpen = false,
AllowsTransparency = true,
VerticalOffset = 350,
HorizontalOffset = 700,
Height = 128,
Width = 128,
};
Canvas c=new Canvas();
c.Background=Brushes.Black;
c.Opacity = 0.6;
Grid p = new Grid();
p.Background = myBrush;
//p.Children.Add(ima);
//p.Children.Add(c);
p.Children.Add(gif);
pop.Child = p;
}
}
EDIT 2:
I have the same question only my code is change. Now I created new xaml.cs for pop up window, and try to achieve the same purpose, but I do not get the same (I talk about brightness decrease).
Its my new xaml.cs :
namespace uploader
{
/// <summary>
/// Interaction logic for PopupPanel.xaml
/// </summary>
public partial class PopupPanel : UserControl
{
private Popup _currentPopup;
public PopupPanel()
{
InitializeComponent();
string path1 = System.AppDomain.CurrentDomain.BaseDirectory + "../../loader_bg.png";
string path2 = System.AppDomain.CurrentDomain.BaseDirectory + "../../loader.gif";
ImageBrush myBrush = new ImageBrush();
Image image = new Image();
image.Source = new BitmapImage(new Uri(path1));
myBrush.ImageSource = image.Source;
MediaElement gif = new MediaElement();
gif.Source=new Uri(path2);
gif.Height = 72;
gif.Width = 72;
_currentPopup = new Popup
{
StaysOpen = false,
AllowsTransparency = true,
VerticalOffset = 350,
HorizontalOffset = 700,
Height = 128,
Width = 128,
};
Overlay.Visibility = Visibility.Visible;
_currentPopup.Closed += PopupClosing;
_currentPopup.IsOpen = true;
Grid p = new Grid();
p.Background = myBrush;
p.Children.Add(gif);
_currentPopup.Child = p;
}
private void PopupClosing(object sender, EventArgs e)
{
_currentPopup.Closed -= PopupClosing;
_currentPopup = null;
Overlay.Visibility = Visibility.Collapsed;
}
}
}
My Mainwindow.xaml.cs:
namespace uploader
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void sample_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
PopupPanel pop = new PopupPanel();
}
...
I do this in all my WPF applications by using a Canvas with black background and opacity
Example:
<Window>
<Grid>
<!--Main content-->
<UserControl/>
<Grid>
<Canvas Background="Black" Opacity="0.6"/>
<!--Overlay content-->
<UserControl VerticalAlignment="Center" HorizontalAlignment="Center"/>
</Grid>
</Grid>
</Window>
Using your current code, you will need to handle the visibility of the Canvas overlay.
It's easier to to have it defined within your XAML as shown below:
<Window>
<Grid>
<!--Main content-->
<UserControl/>
<Grid>
<Canvas x:Name="Overlay"
Background="Black"
Opacity="0.6"
Visibility="Collapsed"/>
<!--Overlay content-->
<UserControl VerticalAlignment="Center" HorizontalAlignment="Center"/>
</Grid>
</Grid>
</Window>
Then, in your code-behind you can set the visibility before the popup opens, and when it closes:
Popup _currentPopup;
private void sample_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
...
_currentPopup = new Popup
{
StaysOpen = false,
AllowsTransparency = true,
VerticalOffset = 350,
HorizontalOffset = 700,
Height = 128,
Width = 128
};
Overlay.Visibility = Visibility.Visible;
_currentPopup.Closed += PopupClosing;
_currentPopup.IsOpen = true;
}
private void PopupClosing(object sender, EventArgs e)
{
_currentPopup.Closed -= PopupClosing;
_currentPopup = null;
Overlay.Visibility = Visibility.Collapsed;
}
Note, that I am using a local variable to keep a reference to the popup. This is so that I can un-subscribe from the Closing event (helps prevent memory leaks)

Dragging an image from the desktop and dropping it to bitmap viewer

I want to drag an image (person image) from the desktop and then dropping it to my wpf application
any resources ?
XAML:
<Window x:Class="_13378018.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" AllowDrop="True" Drop="OnDrop">
<Grid>
<Image x:Name="imageViewer"/>
</Grid>
</Window>
Code-behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
private BitmapImage LoadImageFromFile(string filename)
{
using (var fs = File.OpenRead(filename))
{
var img = new BitmapImage();
img.BeginInit();
img.CacheOption = BitmapCacheOption.OnLoad;
// Downscaling to keep the memory footprint low
img.DecodePixelWidth = (int)SystemParameters.PrimaryScreenWidth;
img.StreamSource = fs;
img.EndInit();
return img;
}
}
private void OnDrop(object sender, DragEventArgs e)
{
var data = e.Data as DataObject;
if (data.ContainsFileDropList())
{
var files = data.GetFileDropList();
imageViewer.Source = LoadImageFromFile(files[0]);
}
}
}

How to create silverlight sequence animation programmatically?

I need to animate a rectangle to move horizontally first, then after 2 second make it move vertically. All this should be done programmatically.
Anybody can help me? Thanks!
Using the following XAML:
<UserControl x:Class="SilverlightApplication1.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Canvas x:Name="LayoutRoot">
<Rectangle x:Name="myBox" Fill="Red" Height="100" Width="100" Canvas.Left="0" Canvas.Top="0" />
</Canvas>
</UserControl>
You could create the animation programatically using this:
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
Loaded += MainPage_Loaded;
}
void MainPage_Loaded(object sender, RoutedEventArgs e)
{
var moveAnimation = CreateAnimation(this.myBox);
moveAnimation.Begin();
}
public Storyboard CreateAnimation(FrameworkElement element)
{
var storyboard = new Storyboard();
var downAnimation = new DoubleAnimationUsingKeyFrames();
Storyboard.SetTarget(downAnimation, element);
Storyboard.SetTargetProperty(downAnimation, new PropertyPath(Canvas.TopProperty));
downAnimation.KeyFrames.Add(new EasingDoubleKeyFrame
{
KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(2)),
Value = 200
});
storyboard.Children.Add(downAnimation);
var overAnimation = new DoubleAnimationUsingKeyFrames();
Storyboard.SetTarget(overAnimation, element);
Storyboard.SetTargetProperty(overAnimation, new PropertyPath(Canvas.LeftProperty));
overAnimation.KeyFrames.Add(new EasingDoubleKeyFrame
{
KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(2)),
Value = 0
});
overAnimation.KeyFrames.Add(new EasingDoubleKeyFrame
{
KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(4)),
Value = 200
});
storyboard.Children.Add(overAnimation);
return storyboard;
}
}

Resources