Daily Demo: Image Stack
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.
XAML-Code:
<UserControl x:Class="ImageStack.MainPage"
xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="https://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="https://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, https://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