Freigeben über


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.

image

Live-Demo here

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