An interesting control is a Image Stack, where Images are stacked on top of each other. Normally you remove the image on top and put it to the back. So I did. If you double-click the stack an animation move the image to the back.
Live-Demo here
XAML-Code:
<UserControl x:Class="ImageStack.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignHeight="321" d:DesignWidth="416" xmlns:my="clr-namespace:TheOliver.Controls"> <Grid x:Name="LayoutRoot" Background="White"> <my:ImageStack Margin="12" x:Name="imageStack1" Background="Gray" > <Image Source="/ImageStack;Component/Assets/1.jpg" /> <Image Source="/ImageStack;Component/Assets/2.jpg" /> <Image Source="/ImageStack;Component/Assets/3.jpg" /> <Image Source="/ImageStack;Component/Assets/4.jpg" /> <Image Source="/ImageStack;Component/Assets/5.jpg" /> </my:ImageStack> </Grid> </UserControl>
Source-Code:
// Copyright © Microsoft Corporation. All Rights Reserved. // This code released under the terms of the // Microsoft Public License (MS-PL, http://opensource.org/licenses/ms-pl.html.) using System; using System.Linq; using System.Windows; using System.Windows.Controls; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Media.Effects; using System.Windows.Threading; namespace TheOliver.Controls { public class ImageStack : Panel { public ImageStack() { _doubleClickTimer = new DispatcherTimer(); _doubleClickTimer.Interval = new TimeSpan(0, 0, 0, 0, DoubleClickSpeedInMS); _doubleClickTimer.Tick += (s, e) => { _clickCount = 0; _doubleClickTimer.Stop(); }; this.MouseLeftButtonUp += (s, e) => { if (_clickCount == 1) { // Doubleclick detected HideCurrentImage(); } else { _clickCount++; _doubleClickTimer.Start(); } }; _hideStoryboard.Completed += (s, e) => { this.Children.Remove(_removedImage); this.Children.Insert(0, _removedImage); ShowNextImage(); }; _showStoryboard.Completed += (s, e) => { _storyboardIsActive = false; }; } Random _random = new Random(); int _clickCount; DispatcherTimer _doubleClickTimer; Storyboard _hideStoryboard = new Storyboard(); Storyboard _showStoryboard = new Storyboard(); Image _removedImage; bool _storyboardIsActive = false; double _newX; double _newY; Size _finalSize; private void ShowNextImage() { TimeSpan duration = new TimeSpan(0, 0, 0, 0, AnimationInMS); // Part 2 DoubleAnimation xa2 = new DoubleAnimation(); xa2.Duration = duration; xa2.To = -_newX; DoubleAnimation ya2 = new DoubleAnimation(); ya2.Duration = duration; ya2.To = -_newY; _showStoryboard.Stop(); _showStoryboard.Children.Clear(); _showStoryboard.Duration = duration; _showStoryboard.Children.Add(xa2); _showStoryboard.Children.Add(ya2); TransformGroup tg = _removedImage.RenderTransform as TransformGroup; TranslateTransform tt2 = new TranslateTransform(); tg.Children.Add(tt2); _removedImage.RenderTransform = tg; Storyboard.SetTarget(xa2, tt2); //set Animation Target Storyboard.SetTargetProperty(xa2, new PropertyPath("X")); // set Animation TargetProperty Storyboard.SetTarget(ya2, tt2); Storyboard.SetTargetProperty(ya2, new PropertyPath("Y")); if (this.Resources.Contains("showme")) { this.Resources.Remove("showme"); } this.Resources.Add("showme", _showStoryboard); _showStoryboard.Begin(); } bool _layouted = false; protected override Size ArrangeOverride(Size finalSize) { _finalSize = finalSize; if (!_layouted) { LayoutImages(_finalSize); _layouted = true; } return base.ArrangeOverride(finalSize); } private void HideCurrentImage() { if (_storyboardIsActive) { return; } _storyboardIsActive = true; _removedImage = this.Children[this.Children.Count - 1] as Image; if (_removedImage != null) { TimeSpan duration = new TimeSpan(0, 0, 0, 0, AnimationInMS); double angle = _random.Next(360); _newX = Math.Sin(angle) * this.RenderSize.Width; _newY = Math.Cos(angle) * this.RenderSize.Height; _hideStoryboard.Stop(); _hideStoryboard.Children.Clear(); // Part 1 DoubleAnimation xa1 = new DoubleAnimation(); xa1.Duration = duration; xa1.To = _newX; DoubleAnimation ya1 = new DoubleAnimation(); ya1.Duration = duration; ya1.To = _newY; _hideStoryboard.Duration = duration; _hideStoryboard.Children.Add(xa1); _hideStoryboard.Children.Add(ya1); TransformGroup tg = _removedImage.RenderTransform as TransformGroup; TranslateTransform tt = new TranslateTransform(); tg.Children.Add(tt); _removedImage.RenderTransform = tg; Storyboard.SetTarget(xa1, tt); Storyboard.SetTargetProperty(xa1, new PropertyPath("X")); Storyboard.SetTarget(ya1, tt); Storyboard.SetTargetProperty(ya1, new PropertyPath("Y")); if (this.Resources.Contains("hideme")) { this.Resources.Remove("hideme"); } this.Resources.Add("hideme", _hideStoryboard); _hideStoryboard.Begin(); } } private Size LayoutImages(Size finalSize) { var images = this.Children.OfType<Image>(); Random rnd = new Random(); foreach (var image in images) { // Drop Shadow Effect if (ShowDropShadowEffect) { DropShadowEffect dse = new DropShadowEffect(); dse.ShadowDepth = 0.3; image.Effect = dse; } // Center image image.VerticalAlignment = System.Windows.VerticalAlignment.Center; image.HorizontalAlignment = System.Windows.HorizontalAlignment.Center; TransformGroup tg = new TransformGroup(); // Scale to fit the finalsize * 90% image.Stretch = Stretch.Uniform; image.Width = finalSize.Width * ResizeFactor; image.Height = finalSize.Height * ResizeFactor; image.Margin = new Thickness( (finalSize.Width - image.Width) / 2, (finalSize.Height - image.Height) / 2, (finalSize.Width - image.Width) / 2, (finalSize.Height - image.Height) / 2); // Rotate image RotateTransform rt = new RotateTransform(); rt.Angle = 90 - rnd.Next(180); rt.CenterX = image.Width / 2; rt.CenterY = image.Height / 2; tg.Children.Add(rt); image.RenderTransform = tg; } return finalSize; } #region Properties public double ResizeFactor { get { return (double)GetValue(ResizeFactorProperty); } set { SetValue(ResizeFactorProperty, value); } } public static readonly DependencyProperty ResizeFactorProperty = DependencyProperty.Register( "ResizeFactor", typeof(double), typeof(ImageStack), new PropertyMetadata(0.5, OnValueChanged)); private static void OnValueChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) { ImageStack stack = sender as ImageStack; stack.InvalidateArrange(); } public bool ShowDropShadowEffect { get { return (bool)GetValue(ShowDropShadowEffectProperty); } set { SetValue(ShowDropShadowEffectProperty, value); } } public static readonly DependencyProperty ShowDropShadowEffectProperty = DependencyProperty.Register( "ShowDropShadowEffect", typeof(bool), typeof(ImageStack), new PropertyMetadata(true, OnValueChanged)); public int DoubleClickSpeedInMS { get { return (int)GetValue(DoubleClickSpeedInMSProperty); } set { SetValue(DoubleClickSpeedInMSProperty, value); } } public static readonly DependencyProperty DoubleClickSpeedInMSProperty = DependencyProperty.Register( "DoubleClickSpeedInMS", typeof(int), typeof(ImageStack), new PropertyMetadata(500, OnDoubleClickSpeedChanged)); private static void OnDoubleClickSpeedChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) { ImageStack stack = sender as ImageStack; stack._doubleClickTimer.Stop(); stack._doubleClickTimer.Interval = new TimeSpan(0, 0, 0, 0, (int)e.NewValue); } public bool EnableDragging { get { return (bool)GetValue(EnableDraggingProperty); } set { SetValue(EnableDraggingProperty, value); } } public static readonly DependencyProperty EnableDraggingProperty = DependencyProperty.Register( "EnableDragging", typeof(bool), typeof(ImageStack), new PropertyMetadata(false)); public int AnimationInMS { get { return (int)GetValue(AnimationInMSProperty); } set { SetValue(AnimationInMSProperty, value); } } public static readonly DependencyProperty AnimationInMSProperty = DependencyProperty.Register( "AnimationInMS", typeof(int), typeof(ImageStack), new PropertyMetadata(125)); #endregion } } Download Sourcecode