WPF Control Flip

13,182

Solution 1

It can be done without 3D. ScaleEffect with changing horizontal scale from 1 to -1 has the same visual effect:

<Image RenderTransformOrigin="0.5,0.5">
    <Image.RenderTransform>
        <ScaleTransform ScaleX="-1" />
    </Image.RenderTransform>
</Image>

You can animate ScaleX property to get rotating effect. You should also change it's Viisibility from Visible to Hidden and vice versa. To make image disappearing after rotating 90 degrees. At the same time back panel should become visible.

Solution 2

A UserControl which is flippable:

<UserControl x:Class="Test.UserControls.FlipControl" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:Test.UserControls" Name="control">
    <UserControl.Resources>
        <ContentControl x:Key="BackSide" Content="{Binding Source={x:Reference control}, Path=Back}" RenderTransformOrigin="0.5,0.5">
            <ContentControl.RenderTransform>
                <ScaleTransform ScaleX="-1" />
            </ContentControl.RenderTransform>
        </ContentControl>
    </UserControl.Resources>
    <ContentControl RenderTransformOrigin="0.5,0.5">
        <ContentControl.RenderTransform>
            <TransformGroup>
                <ScaleTransform x:Name="transform" ScaleX="1" />
            </TransformGroup>
        </ContentControl.RenderTransform>
        <ContentControl.Style>
            <Style TargetType="{x:Type ContentControl}">
                <Setter Property="Content" Value="{Binding ElementName=control, Path=Front}" />
                <Style.Triggers>
                    <DataTrigger Value="True">
                        <DataTrigger.Binding>
                            <Binding ElementName="transform" Path="ScaleX">
                                <Binding.Converter>
                                    <local:LessThanXToTrueConverter X="0" />
                                </Binding.Converter>
                            </Binding>
                        </DataTrigger.Binding>
                        <DataTrigger.Setters>
                            <Setter Property="Content" Value="{StaticResource BackSide}"/>
                        </DataTrigger.Setters>
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </ContentControl.Style>
    </ContentControl>
</UserControl>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.ComponentModel;
using System.Windows.Media.Animation;

namespace Test.UserControls
{
    /// <summary>
    /// Interaction logic for FlipControl.xaml
    /// </summary>
    public partial class FlipControl : UserControl, INotifyPropertyChanged
    {
        public static readonly DependencyProperty FrontProperty =
            DependencyProperty.Register("Front", typeof(UIElement), typeof(FlipControl), new UIPropertyMetadata(null));
        public UIElement Front
        {
            get { return (UIElement)GetValue(FrontProperty); }
            set { SetValue(FrontProperty, value); }
        }

        public static readonly DependencyProperty BackProperty =
            DependencyProperty.Register("Back", typeof(UIElement), typeof(FlipControl), new UIPropertyMetadata(null));
        public UIElement Back
        {
            get { return (UIElement)GetValue(BackProperty); }
            set { SetValue(BackProperty, value); }
        }

        public static readonly DependencyProperty FlipDurationProperty =
            DependencyProperty.Register("FlipDuration", typeof(Duration), typeof(FlipControl), new UIPropertyMetadata((Duration)TimeSpan.FromSeconds(0.5)));
        public Duration FlipDuration
        {
            get { return (Duration)GetValue(FlipDurationProperty); }
            set { SetValue(FlipDurationProperty, value); }
        }

        private bool _isFlipped = false;
        public bool IsFlipped
        {
            get { return _isFlipped; }
            private set
            {
                if (value != _isFlipped)
                {
                    _isFlipped = value;
                    OnPropertyChanged(new PropertyChangedEventArgs("IsFlipped"));
                }
            }
        }

        private IEasingFunction EasingFunction = new SineEase() { EasingMode = EasingMode.EaseInOut };

        public FlipControl()
        {
            InitializeComponent();
        }

        public void Flip()
        {
            var animation = new DoubleAnimation()
            {
                Duration = FlipDuration,
                EasingFunction = EasingFunction,
            };
            animation.To = IsFlipped ? 1 : -1;
            transform.BeginAnimation(ScaleTransform.ScaleXProperty, animation);
            IsFlipped = !IsFlipped;
            OnFlipped(new EventArgs());
        }

        public event EventHandler Flipped;

        protected virtual void OnFlipped(EventArgs e)
        {
            if (this.Flipped != null)
            {
                this.Flipped(this, e);
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
        {
            if (this.PropertyChanged != null)
            {
                this.PropertyChanged(this, e);
            }
        }
    }

    public class LessThanXToTrueConverter : IValueConverter
    {
        public double X { get; set; }

        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            return (double)value < X;
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotSupportedException();
        }
    }
}

Usage example:

<uc:FlipControl x:Name="fc">
    <uc:FlipControl.Front>
        <Image Source="/Images/Default.ico" />
    </uc:FlipControl.Front>
    <uc:FlipControl.Back>
        <Image Source="/Images/Error.ico" />
    </uc:FlipControl.Back>
</uc:FlipControl>
fc.Flip();

Solution 3

old post I know, but take a look at http://thriple.codeplex.com/

Josh smith provided a control that did this back in 2009.

Solution 4

Have a look at this project.

I am waiting for Silverlight's plane projections coming to WPF.

Share:
13,182
CatBusStop
Author by

CatBusStop

Updated on August 08, 2022

Comments

  • CatBusStop
    CatBusStop almost 2 years

    I've got a control that is mainly comprised of an image and a button. I want to display the image meta data on the back of the image and have the control flip horizontally when the button is pressed:

    i.e.

    enter image description here

    Click the "info" button...

    enter image description here

    Rotate the image 180 degs around the axis...

    enter image description here

    Show "back" of image with meta data (or whatever really).

    Obviously when the red "close" button is clicked, the image rotates around the final 180 degs so that the image is showing again.

    I've not done any 3D really in XAML, but I can't see why this would be too complex...

  • H.B.
    H.B. about 13 years
    This only flips the image, the asker's image has a backside.
  • metalcam
    metalcam about 13 years
    Yes, but couldn't he simulate an appearing of the backside of the image, using the same effect, but in the other way ?
  • CatBusStop
    CatBusStop about 13 years
    Yeah, I've been trying X-Scale transforms and while it gives an appearance of rotation, because the control doesn't skew, it seems to look quite flat. I'll continue investigating, but might well settle on this for simplicity's sake :)
  • CatBusStop
    CatBusStop about 13 years
    I can, but I think ScaleX alone makes the rotation look very flat... Just trying to figure out what skew is needed to give it some depth :)
  • H.B.
    H.B. about 13 years
    I "fixed" the flipped backside problem by flipping it twice; You are right about it looking flat, but i doubt that a simple skew transform will make it look good, a perspective transform would be better.
  • H.B.
    H.B. about 13 years
    The interaction logic of that thing is abysmal, so while the idea might be fine i would suggest pretty much completely rewriting it.
  • H.B.
    H.B. about 13 years
    Further are you sure you can create a RenderTransform which behaves like that?
  • CatBusStop
    CatBusStop about 13 years
    Yeah, it needs to shear really to get the proper perspective and to do that requires 3D. I'll investigate into it further when I can justify spending more time on the UI :)
  • Manish Dubey
    Manish Dubey almost 8 years
    +1 for animation but on using separate controls instead of images as a UIElement, back control is not working...