Archive

Archive for the ‘Silverlight 3’ Category

[SL3] : 3D ControlRotator

Nous allons voir dans cet article comment réaliser un composant permettant d’afficher deux contrôles distincts via des rotations. Ce principe est utilisé pour les gadgets mac par exemple.

Voici ce que nous obtiendrons

L’énumération des directions disponibles

 
 /// <summary>
    /// Enumération définissant le sens de rotation du controle
    /// </summary>
    public enum RotateDirection
    {
        /// <summary>
        /// De la gauche vers la droite
        /// </summary>
        LeftToRight,
        /// <summary>
        /// De la droite vers la gauche
        /// </summary>
		RightToLeft,
        /// <summary>
        /// Tu haut vers le bas
        /// </summary>
		TopToBottom,
        /// <summary>
        /// Du bas vers le haut
        /// </summary>
		BottomToTop
    }

Realisation du controle de rotation

Les dependency properties

Crééons les DP permettant de déterminer :

  • L’élément de devant
  • L’élément de derrière
  • Si le controle effectue une rotation
  • La direction de la rotation
  • La durée de la rotation
  • Si le controle à la face avant en vue
   /// <summary>
        /// Définit la DP représentant le contenu de la face principale
        /// </summary>
        private static readonly DependencyProperty FrontContentProperty =
            DependencyProperty.Register(
            "FrontContent",
            typeof(UIElement),
            typeof(ContentControl3D),
            new PropertyMetadata(null));
        /// <summary>
        /// Définit la DP représentant si le controle est en train de faire une rotation
        /// </summary>
        private static readonly DependencyProperty IsRotatingProperty =
           DependencyProperty.Register(
           "IsRotating",
           typeof(Boolean),
           typeof(ContentControl3D),
           new PropertyMetadata(null));
        /// <summary>
        /// Définit la DP définissant si le controle affiche le controle de la face principale
        /// </summary>
        private static readonly DependencyProperty IsFrontInViewProperty =
          DependencyProperty.Register(
          "IsFrontInView",
          typeof(Boolean),
          typeof(ContentControl3D),
          new PropertyMetadata(true));
        /// <summary>
        /// Définit la DP représentant le contenu de la face cachée
        /// </summary>
        private static readonly DependencyProperty BackContentProperty =
           DependencyProperty.Register(
           "BackContent",
           typeof(UIElement),
           typeof(ContentControl3D),
           new PropertyMetadata(null));
        /// <summary>
        /// Définit la DP représentant la durée de l'animation de rotation (en ms)
        /// </summary>
        private static readonly DependencyProperty DurationProperty =
           DependencyProperty.Register(
           "Duration",
           typeof(Double),
           typeof(ContentControl3D),
           new PropertyMetadata(600.0));
        /// <summary>
        /// Définit la DP représentant le sens de rotation du controle
        /// </summary>
        private static readonly DependencyProperty DirectionProperty =
           DependencyProperty.Register(
           "Direction",
           typeof(RotateDirection),
           typeof(ContentControl3D),
           new PropertyMetadata(null));
  /// <summary>
        /// Obtient ou définit le ContentPresenter associé à la face principale
        /// </summary>
        public UIElement FrontContent
        {
            get
            {
                return (UIElement)GetValue(FrontContentProperty);
            }
            set
            {
                SetValue(FrontContentProperty, value);
            }
        }
        /// <summary>
        /// Obtient ou définit le ContentPresenter associé à la face cachée
        /// </summary>
        public UIElement BackContent
        {
            get
            {
                return (UIElement)GetValue(BackContentProperty);
            }
            set
            {
                SetValue(BackContentProperty, value);
            }
        }
        /// <summary>
        /// Obtient ou définit le sens de rotation du controle
        /// </summary>
        public RotateDirection Direction
        {
            get
            {
                return (RotateDirection)GetValue(DirectionProperty);
            }
            set
            {
                SetValue(DirectionProperty, value);
            }
        }
        /// <summary>
        /// Obtient ou définit si le controle est en train d'effectuer une rotation
        /// </summary>
        public Boolean IsRotating
        {
            get
            {
                return (Boolean)GetValue(IsRotatingProperty);
            }
            set
            {
                SetValue(IsRotatingProperty, value);
            }
        }
        /// <summary>
        /// Obtient ou définit la durée de l'animation de rotation
        /// </summary>
        public Double Duration
        {
            get
            {
                return (Double)GetValue(DurationProperty);
            }
            set
            {
                SetValue(DurationProperty, value);
            }
        }
        /// <summary>
        /// Détermine si le controle à le FrontContent en visuel
        /// </summary>
        public Boolean IsFrontInView
        {
            get
            {
                return (Boolean)GetValue(IsFrontInViewProperty);
            }
            set
            {
                SetValue(IsFrontInViewProperty, value);
            }
        }

Fonctions de calcul des rotations

    private double GetXRotation()
        {
            double rot = 0;
            switch (Direction)
            {
                case RotateDirection.BottomToTop:
                    rot = -180;
                    break;
                case RotateDirection.TopToBottom:
                    rot = 180;
                    break;
            }
            return rot;
        }
        private double GetYRotation()
        {
            double rot = 0;
            switch (Direction)
            {
                case RotateDirection.RightToLeft:
                    rot = 180;
                    break;
                case RotateDirection.LeftToRight:
                    rot = -180;
                    break;
            }
            return rot;
        }
        /// <summary>
        /// Effectue une rotation (uniquement si aucune action courrante n'est en cours)
        /// </summary>
        public void Rotate()
        {
            if (IsRotating == false)
            {
                GenerateAnimations();
                IsRotating = true;
                IsFrontInView = !IsFrontInView;
                switch (Direction)
                {
                    case RotateDirection.LeftToRight:
                    case RotateDirection.RightToLeft:
                        _animationY.Begin();
                        break;
                    case RotateDirection.BottomToTop:
                    case RotateDirection.TopToBottom:
                        _animationX.Begin();
                        break;
                }
            }
        }

Fonctions de génération des animations de rotation

     private void GenerateAnimations()
        {
            DoubleAnimation daY = null, daX = null;
            Boolean visibilityIncluded =false;
            #region Init / CleanUp Storyboard
            if (_animationY != null)
            {
                _animationY.Stop();
                _animationY.Children.Clear();
            }
            if (_animationX != null)
            {
                _animationX.Stop();
                _animationX.Children.Clear();
            }
            #endregion
            if (IsFrontInView)
            {
                _backContentPresenter.Visibility = Visibility.Collapsed;
                _frontContentPresenter.Visibility = Visibility.Visible;
            }
            else
            {
                _frontContentPresenter.Visibility = Visibility.Collapsed;
                _backContentPresenter.Visibility = Visibility.Visible;
            }
            #region Rotation
            if (GetYRotation() != 0) // détermine si on a besoin d'une rotation Y
            {
                daY = new DoubleAnimation()
                {
                    By = GetYRotation(),
                    Duration = new Duration(new TimeSpan(0, 0, 0, 0, (Int32)Duration)),
                    EasingFunction = new PowerEase()
                    {
                        Power = 5,
                        EasingMode = EasingMode.EaseInOut
                    }
                };
                Storyboard.SetTarget(daY, this);
                daY.SetValue(Storyboard.TargetPropertyProperty, new PropertyPath("(UIElement.Projection).(PlaneProjection.RotationY)"));
            }
            if (GetXRotation() != 0)// détermine si on a besoin d'une rotation X
            {
                daX = new DoubleAnimation()
              {
                  By = GetXRotation(),
                  Duration = new Duration(new TimeSpan(0, 0, 0, 0, (Int32)Duration)),
                  EasingFunction = new PowerEase()
                  {
                      Power = 5,
                      EasingMode = EasingMode.EaseInOut
                  }
              };
                Storyboard.SetTarget(daX, this);
                daX.SetValue(Storyboard.TargetPropertyProperty, new PropertyPath("(UIElement.Projection).(PlaneProjection.RotationX)"));
            }
            #endregion
            #region Visibility
            ObjectAnimationUsingKeyFrames kf = new ObjectAnimationUsingKeyFrames();
            ObjectAnimationUsingKeyFrames kf2 = new ObjectAnimationUsingKeyFrames();
            kf.KeyFrames.Add(new DiscreteObjectKeyFrame()
            {
                Value = Visibility.Collapsed,
                KeyTime = new TimeSpan(0, 0, 0, 0, ((Int32)Duration / 2))
            });
            kf2.KeyFrames.Add(new DiscreteObjectKeyFrame()
            {
                Value = Visibility.Visible,
                KeyTime = new TimeSpan(0, 0, 0, 0, ((Int32)Duration / 2))
            });
            Storyboard.SetTarget(kf, (IsFrontInView) ? (_frontContentPresenter) : (_backContentPresenter));
            kf.SetValue(Storyboard.TargetPropertyProperty, new PropertyPath("(UIElement.Visibility)"));
            Storyboard.SetTarget(kf2, (IsFrontInView) ? (_backContentPresenter) : (_frontContentPresenter));
            kf2.SetValue(Storyboard.TargetPropertyProperty, new PropertyPath("(UIElement.Visibility)"));
            #endregion
            #region Scaling
            DoubleAnimationUsingKeyFrames daScaleX = new DoubleAnimationUsingKeyFrames();
            daScaleX.KeyFrames.Add(new EasingDoubleKeyFrame()
            {
                Value = -1,
                KeyTime = new TimeSpan(0, 0, 0, 0, ((Int32)Duration / 2))
            });
            DoubleAnimationUsingKeyFrames daScaleY = new DoubleAnimationUsingKeyFrames();
            daScaleY.KeyFrames.Add(new EasingDoubleKeyFrame()
            {
                Value = -1,
                KeyTime = new TimeSpan(0, 0, 0, 0, ((Int32)Duration / 2))
            });
            Storyboard.SetTarget(daScaleX, (IsFrontInView) ? (_backContentPresenter) : (_frontContentPresenter));
            daScaleX.SetValue(Storyboard.TargetPropertyProperty, new PropertyPath("(UIElement.RenderTransform).(ScaleTransform.ScaleX)"));
            Storyboard.SetTarget(daScaleY, (IsFrontInView) ? (_backContentPresenter) : (_frontContentPresenter));
            daScaleY.SetValue(Storyboard.TargetPropertyProperty, new PropertyPath("(UIElement.RenderTransform).(ScaleTransform.ScaleY)"));
            #endregion
            if (daX != null)
            {
                visibilityIncluded = true;
                _animationX.Children.Add(kf);
                _animationX.Children.Add(kf2);
                _animationX.Children.Add(daScaleY);
                _animationX.Children.Add(daX);
            }
            if (daY != null)
            {
                if (!visibilityIncluded)
                {
                    _animationY.Children.Add(kf);
                    _animationY.Children.Add(kf2);
                }
                _animationY.Children.Add(daScaleX);
                _animationY.Children.Add(daY);
            }
        }

Code source complet

 /// <summary>
    /// Controle permettant d'afficher un controle caché via une animation de rotation
    /// </summary>
    public class ContentControl3D : Grid
    {
        #region Membres
        private Grid _rootLayout = new Grid();
        private ContentControl _frontContentPresenter = new ContentControl();
        private ContentControl _backContentPresenter = new ContentControl();
        private PlaneProjection _projection = null;
        private Storyboard _animationX = new Storyboard();
        private Storyboard _animationY = new Storyboard();
        /// <summary>
        /// Définit la DP représentant le contenu de la face principale
        /// </summary>
        private static readonly DependencyProperty FrontContentProperty =
            DependencyProperty.Register(
            "FrontContent",
            typeof(UIElement),
            typeof(ContentControl3D),
            new PropertyMetadata(null));
        /// <summary>
        /// Définit la DP représentant si le controle est en train de faire une rotation
        /// </summary>
        private static readonly DependencyProperty IsRotatingProperty =
           DependencyProperty.Register(
           "IsRotating",
           typeof(Boolean),
           typeof(ContentControl3D),
           new PropertyMetadata(null));
        /// <summary>
        /// Définit la DP définissant si le controle affiche le controle de la face principale
        /// </summary>
        private static readonly DependencyProperty IsFrontInViewProperty =
          DependencyProperty.Register(
          "IsFrontInView",
          typeof(Boolean),
          typeof(ContentControl3D),
          new PropertyMetadata(true));
        /// <summary>
        /// Définit la DP représentant le contenu de la face cachée
        /// </summary>
        private static readonly DependencyProperty BackContentProperty =
           DependencyProperty.Register(
           "BackContent",
           typeof(UIElement),
           typeof(ContentControl3D),
           new PropertyMetadata(null));
        /// <summary>
        /// Définit la DP représentant la durée de l'animation de rotation (en ms)
        /// </summary>
        private static readonly DependencyProperty DurationProperty =
           DependencyProperty.Register(
           "Duration",
           typeof(Double),
           typeof(ContentControl3D),
           new PropertyMetadata(600.0));
        /// <summary>
        /// Définit la DP représentant le sens de rotation du controle
        /// </summary>
        private static readonly DependencyProperty DirectionProperty =
           DependencyProperty.Register(
           "Direction",
           typeof(RotateDirection),
           typeof(ContentControl3D),
           new PropertyMetadata(null));
        #endregion
        #region Ctors
        /// <summary>
        /// Crée une instance du controle permettant d'afficher un controle caché via une animation de rotation
        /// </summary>
        public ContentControl3D()
        {
            Loaded += new RoutedEventHandler(OnLoaded);
            _animationX.Completed += new EventHandler(OnAnimationXCompleted);
            _animationY.Completed += new EventHandler(OnAnimationYCompleted);
        }
        #endregion
        #region Méthodes
        private void GenerateAnimations()
        {
            DoubleAnimation daY = null, daX = null;
            Boolean visibilityIncluded =false;
            #region Init / CleanUp Storyboard
            if (_animationY != null)
            {
                _animationY.Stop();
                _animationY.Children.Clear();
            }
            if (_animationX != null)
            {
                _animationX.Stop();
                _animationX.Children.Clear();
            }
            #endregion
            if (IsFrontInView)
            {
                _backContentPresenter.Visibility = Visibility.Collapsed;
                _frontContentPresenter.Visibility = Visibility.Visible;
            }
            else
            {
                _frontContentPresenter.Visibility = Visibility.Collapsed;
                _backContentPresenter.Visibility = Visibility.Visible;
            }
            #region Rotation
            if (GetYRotation() != 0) // détermine si on a besoin d'une rotation Y
            {
                daY = new DoubleAnimation()
                {
                    By = GetYRotation(),
                    Duration = new Duration(new TimeSpan(0, 0, 0, 0, (Int32)Duration)),
                    EasingFunction = new PowerEase()
                    {
                        Power = 5,
                        EasingMode = EasingMode.EaseInOut
                    }
                };
                Storyboard.SetTarget(daY, this);
                daY.SetValue(Storyboard.TargetPropertyProperty, new PropertyPath("(UIElement.Projection).(PlaneProjection.RotationY)"));
            }
            if (GetXRotation() != 0)// détermine si on a besoin d'une rotation X
            {
                daX = new DoubleAnimation()
              {
                  By = GetXRotation(),
                  Duration = new Duration(new TimeSpan(0, 0, 0, 0, (Int32)Duration)),
                  EasingFunction = new PowerEase()
                  {
                      Power = 5,
                      EasingMode = EasingMode.EaseInOut
                  }
              };
                Storyboard.SetTarget(daX, this);
                daX.SetValue(Storyboard.TargetPropertyProperty, new PropertyPath("(UIElement.Projection).(PlaneProjection.RotationX)"));
            }
            #endregion
            #region Visibility
            ObjectAnimationUsingKeyFrames kf = new ObjectAnimationUsingKeyFrames();
            ObjectAnimationUsingKeyFrames kf2 = new ObjectAnimationUsingKeyFrames();
            kf.KeyFrames.Add(new DiscreteObjectKeyFrame()
            {
                Value = Visibility.Collapsed,
                KeyTime = new TimeSpan(0, 0, 0, 0, ((Int32)Duration / 2))
            });
            kf2.KeyFrames.Add(new DiscreteObjectKeyFrame()
            {
                Value = Visibility.Visible,
                KeyTime = new TimeSpan(0, 0, 0, 0, ((Int32)Duration / 2))
            });
            Storyboard.SetTarget(kf, (IsFrontInView) ? (_frontContentPresenter) : (_backContentPresenter));
            kf.SetValue(Storyboard.TargetPropertyProperty, new PropertyPath("(UIElement.Visibility)"));
            Storyboard.SetTarget(kf2, (IsFrontInView) ? (_backContentPresenter) : (_frontContentPresenter));
            kf2.SetValue(Storyboard.TargetPropertyProperty, new PropertyPath("(UIElement.Visibility)"));
            #endregion
            #region Scaling
            DoubleAnimationUsingKeyFrames daScaleX = new DoubleAnimationUsingKeyFrames();
            daScaleX.KeyFrames.Add(new EasingDoubleKeyFrame()
            {
                Value = -1,
                KeyTime = new TimeSpan(0, 0, 0, 0, ((Int32)Duration / 2))
            });
            DoubleAnimationUsingKeyFrames daScaleY = new DoubleAnimationUsingKeyFrames();
            daScaleY.KeyFrames.Add(new EasingDoubleKeyFrame()
            {
                Value = -1,
                KeyTime = new TimeSpan(0, 0, 0, 0, ((Int32)Duration / 2))
            });
            Storyboard.SetTarget(daScaleX, (IsFrontInView) ? (_backContentPresenter) : (_frontContentPresenter));
            daScaleX.SetValue(Storyboard.TargetPropertyProperty, new PropertyPath("(UIElement.RenderTransform).(ScaleTransform.ScaleX)"));
            Storyboard.SetTarget(daScaleY, (IsFrontInView) ? (_backContentPresenter) : (_frontContentPresenter));
            daScaleY.SetValue(Storyboard.TargetPropertyProperty, new PropertyPath("(UIElement.RenderTransform).(ScaleTransform.ScaleY)"));
            #endregion
            if (daX != null)
            {
                visibilityIncluded = true;
                _animationX.Children.Add(kf);
                _animationX.Children.Add(kf2);
                _animationX.Children.Add(daScaleY);
                _animationX.Children.Add(daX);
            }
            if (daY != null)
            {
                if (!visibilityIncluded)
                {
                    _animationY.Children.Add(kf);
                    _animationY.Children.Add(kf2);
                }
                _animationY.Children.Add(daScaleX);
                _animationY.Children.Add(daY);
            }
        }
        private double GetXRotation()
        {
            double rot = 0;
            switch (Direction)
            {
                case RotateDirection.BottomToTop:
                    rot = -180;
                    break;
                case RotateDirection.TopToBottom:
                    rot = 180;
                    break;
            }
            return rot;
        }
        private double GetYRotation()
        {
            double rot = 0;
            switch (Direction)
            {
                case RotateDirection.RightToLeft:
                    rot = 180;
                    break;
                case RotateDirection.LeftToRight:
                    rot = -180;
                    break;
            }
            return rot;
        }
        /// <summary>
        /// Effectue une rotation (uniquement si aucune action courrante n'est en cours)
        /// </summary>
        public void Rotate()
        {
            if (IsRotating == false)
            {
                GenerateAnimations();
                IsRotating = true;
                IsFrontInView = !IsFrontInView;
                switch (Direction)
                {
                    case RotateDirection.LeftToRight:
                    case RotateDirection.RightToLeft:
                        _animationY.Begin();
                        break;
                    case RotateDirection.BottomToTop:
                    case RotateDirection.TopToBottom:
                        _animationX.Begin();
                        break;
                }
            }
        }
        #endregion
        #region Handlers
        void OnLoaded(object sender, RoutedEventArgs e)
        {
            Loaded -= OnLoaded;
            _frontContentPresenter.RenderTransformOrigin = new Point(0.5, 0.5);
            _frontContentPresenter.RenderTransform = new ScaleTransform();
            _frontContentPresenter.HorizontalContentAlignment = HorizontalAlignment.Stretch;
            _frontContentPresenter.VerticalContentAlignment = VerticalAlignment.Stretch;
            _backContentPresenter.RenderTransformOrigin = new Point(0.5, 0.5);
            _backContentPresenter.RenderTransform = new ScaleTransform();
            _backContentPresenter.HorizontalContentAlignment = HorizontalAlignment.Stretch;
            _backContentPresenter.VerticalContentAlignment = VerticalAlignment.Stretch;
            _backContentPresenter.Visibility = Visibility.Collapsed;
            _projection = new PlaneProjection();
            Projection = _projection;
            Children.Add(_backContentPresenter);
            Children.Add(_frontContentPresenter);
            Children.Add(_rootLayout);
            _frontContentPresenter.Visibility = Visibility.Visible;
            _backContentPresenter.Visibility = Visibility.Collapsed;
            _backContentPresenter.Content = BackContent;
            _frontContentPresenter.Content = FrontContent;
        }
        void OnAnimationYCompleted(object sender, EventArgs e)
        {
            Storyboard s = sender as Storyboard;
            if (s != null)
            {
                s.Completed -= OnAnimationXCompleted;
            }
            IsRotating = false;
        }
        void OnAnimationXCompleted(object sender, EventArgs e)
        {
            Storyboard s = sender as Storyboard;
            if (s != null)
            {
                s.Completed -= OnAnimationYCompleted;
            }
            IsRotating = false;
        }
        #endregion
        #region DP
        /// <summary>
        /// Obtient ou définit le ContentPresenter associé à la face principale
        /// </summary>
        public UIElement FrontContent
        {
            get
            {
                return (UIElement)GetValue(FrontContentProperty);
            }
            set
            {
                SetValue(FrontContentProperty, value);
            }
        }
        /// <summary>
        /// Obtient ou définit le ContentPresenter associé à la face cachée
        /// </summary>
        public UIElement BackContent
        {
            get
            {
                return (UIElement)GetValue(BackContentProperty);
            }
            set
            {
                SetValue(BackContentProperty, value);
            }
        }
        /// <summary>
        /// Obtient ou définit le sens de rotation du controle
        /// </summary>
        public RotateDirection Direction
        {
            get
            {
                return (RotateDirection)GetValue(DirectionProperty);
            }
            set
            {
                SetValue(DirectionProperty, value);
            }
        }
        /// <summary>
        /// Obtient ou définit si le controle est en train d'effectuer une rotation
        /// </summary>
        public Boolean IsRotating
        {
            get
            {
                return (Boolean)GetValue(IsRotatingProperty);
            }
            set
            {
                SetValue(IsRotatingProperty, value);
            }
        }
        /// <summary>
        /// Obtient ou définit la durée de l'animation de rotation
        /// </summary>
        public Double Duration
        {
            get
            {
                return (Double)GetValue(DurationProperty);
            }
            set
            {
                SetValue(DurationProperty, value);
            }
        }
        /// <summary>
        /// Détermine si le controle à le FrontContent en visuel
        /// </summary>
        public Boolean IsFrontInView
        {
            get
            {
                return (Boolean)GetValue(IsFrontInViewProperty);
            }
            set
            {
                SetValue(IsFrontInViewProperty, value);
            }
        }
        #endregion
    }

Exemple d’utilisation coté XAML

<controls:ContentControl3D x:Name="Rotator"
    			Width="300"
    			Height="300"
    			Background="Gray"
    			Direction="{Binding SelectedItem, ElementName=comboBox, Mode=OneWay}"
                                   d:LayoutOverrides="GridBox">
    			<controls:ContentControl3D.FrontContent>
    				<!-- face avant -->
    			</controls:ContentControl3D.FrontContent>
    			<controls:ContentControl3D.BackContent>
    				<!-- face arrière -->
    			</controls:ContentControl3D.BackContent>
    		</controls:ContentControl3D>

Réalisation d’une application d’exemple

Code XAML

<UserControl
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:controls="clr-namespace:SL3DContentControl" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" x:Class="SL3DContentControl.MainPage"
             mc:Ignorable="d">
    <Grid x:Name="LayoutRoot"
          Background="#FFE0E0E0">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition />
        </Grid.RowDefinitions>
        <Border Margin="5"
                VerticalAlignment="Top"
                Background="White">
            <Border.Effect>
                <DropShadowEffect ShadowDepth="0"
                                  Opacity="0.8" />
            </Border.Effect>
            <StackPanel Height="104"
                        Margin="5">
                <TextBlock Text="Direction"
                           TextWrapping="Wrap"
                           FontWeight="Bold" />
                <ComboBox x:Name="comboBox"
                          Width="300"
                          SelectedIndex="0"
                          HorizontalAlignment="Left"
                          Margin="0,3,0,0">
                    <controls:RotateDirection>LeftToRight</controls:RotateDirection>
                    <controls:RotateDirection>RightToLeft</controls:RotateDirection>
                    <controls:RotateDirection>TopToBottom</controls:RotateDirection>
                    <controls:RotateDirection>BottomToTop</controls:RotateDirection>
                </ComboBox>
                <TextBlock Text="Durée"
                           TextWrapping="Wrap"
                           FontWeight="Bold"
                           Margin="0,3,0,0" />
                <Slider Minimum="300"
                        Maximum="3000"
                        Value="{Binding Duration, ElementName=Rotator, Mode=TwoWay}"
                        Width="300"
                        HorizontalAlignment="Left"
                        Margin="0,3,0,0" />
                <Button HorizontalAlignment="Left"
                        Margin="0,3,0,0"
                        Content="Rotation !"
                        Click="Button_Click" />
            </StackPanel>
        </Border>
        <Border Margin="5"
                Grid.Row="1">
        	<Border.Background>
        		<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
        			<GradientStop Color="#FFEAEAEA" Offset="0"/>
        			<GradientStop Color="#FF8E8E8E" Offset="1"/>
        		</LinearGradientBrush>
        	</Border.Background>
            <Border.Effect>
                <DropShadowEffect ShadowDepth="0"
                                  Opacity="0.8" />
            </Border.Effect>
        </Border>
    	<Grid Grid.Row="1" Margin="10" HorizontalAlignment="Center" VerticalAlignment="Center">
    		<controls:ContentControl3D x:Name="Rotator"
    			Width="300"
    			Height="300"
    			Background="Gray"
    			Direction="{Binding SelectedItem, ElementName=comboBox, Mode=OneWay}"
                                   d:LayoutOverrides="GridBox">
    			<controls:ContentControl3D.FrontContent>
    				<Grid>
    					<Border Background="White">
    						<Image Source="microsoft_silverlight_c.jpg"
    							Stretch="Uniform"
    							VerticalAlignment="Center"
    							HorizontalAlignment="Center"
    							Margin="5" />
    					</Border>
    					<Button Content="Detail"
    						Margin="0,0,5,5"
    						Click="Button_Click"
    						HorizontalAlignment="Right"
    						VerticalAlignment="Bottom" />
    				</Grid>
    			</controls:ContentControl3D.FrontContent>
    			<controls:ContentControl3D.BackContent>
    				<Border Background="White">
    					<Grid Margin="5">
    						<Grid.RowDefinitions>
    							<RowDefinition />
    							<RowDefinition Height="Auto" />
    						</Grid.RowDefinitions>
    						<TextBlock Text="Silverlight est une technologie permettant de développer et d'exécuter des Applications Internet Riches (RIA) dans les principaux navigateurs web disponibles aujourd'hui. Un des apports fondamentaux de la version 2 est la possibilité de tirer partie du .NET Framework."
    							VerticalAlignment="Top"
    							TextWrapping="Wrap"
    							ScrollViewer.VerticalScrollBarVisibility="Auto" />
    						<HyperlinkButton HorizontalAlignment="Right"
    							VerticalAlignment="Bottom"
    							Content="Silverlight"
    							Grid.Row="1"
    							NavigateUri="http://silverlight.net/"
    							TargetName="_blank" />
    						<Button Content="&amp;lt;- Retour"
    							Margin="5,5,0,0"
    							Click="Button_Click"
    							Grid.Row="1"
    							HorizontalAlignment="Left"
    							VerticalAlignment="Bottom" />
    					</Grid>
    				</Border>
    			</controls:ContentControl3D.BackContent>
    		</controls:ContentControl3D>
    	</Grid>
    </Grid>
</UserControl>

Code Behind

using System.Windows;
using System.Windows.Controls;
namespace SL3DContentControl
{
    public partial class MainPage : UserControl
    {
        public MainPage()
        {
            InitializeComponent();
        }
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            Rotator.Rotate();
        }
    }
}

Voilà à présent vous disposez d’un moyent élégant d’afficher un cotrole caché via une animation de rotation 🙂
Code source complet + application d’exemple SL3DContentControl.zip

Publicités
Catégories :Silverlight, Silverlight 3

[SL3] : Bings Maps en Silverlight !

Lors d’un précédent post je m’interrogeais sur le fait que microsoft n’utilisais pas le tout nouveau contrôle maps tant il était exceptionnel.

C’est chose faite, à présent http://www.bing.com/maps/explore utilise la technologie Silverlight 3 ! et devient ainsi bien plus agréable que google maps 🙂

Au menu animations, Bird eye, photosynth, etc !

Voici deux quelques d’écran :

image image

image

image

Catégories :Silverlight, Silverlight 3

[SL3] : Victoria’s Secret

Un nouveau site silverlight vient de voir le jour et il n’est pas le plus déplaisant à regarder !

image image

Plus d’informations sur le site officiel ici

Catégories :Silverlight, Silverlight 3

[SL3] : JPG thumbnail d’une image JPG ou PNG en Silverlight 3

Update 22.11.2009 : Avec cette technique vous pouvez aussi réaliser une miniature de fichiers PNG mais la sortie sera un fichier jpg.  Modifiez la ligne par : ofd.Filter = « Fichiers image|*.jpg;*.jpeg;*.png »;

La version actuelle de silverlight ne propose pas (nativement) de composants permettant l’encodage d’images comme on pourrait le faire en WPF. Cela dit certains projets permettent à présent de réaliser cela mais la tache n’est pas forcement aisée. Cette technique peut être utilisé afin de compresser et retailler une image avant de l’envoyer au serveur (via un service WCF par exemple), les gains sont multiples : 

  • utilisation de la bande passante réduite au minimum
  • réactivité de l’application
  • meilleur disponibilité de la plateforme de service et donc des serveurs

Ayant été confronté à ce problème récemment, je vous propose mon retour sur expérience dans le domaine ! 

Pré requis

Librairie FJCore (permettant d’encoder des fichiers en JPG) que vous pourrez trouver ici 

La classe ImageHelpers !

Voici ma classe utilitaire permettant de compresser et retailler facilement une image provenant d’un stream (exemple d’un OpenFileDialog). Il est question ici d’une classe qui a fait son apparition dans Silverlight 3 (connue en WPF) : WritableBitmap. La clé de la réussite résulte dans le rendu du controle image dans un WritableBitmap et de convertir celui-ci via la librairie FJCore afin d’obtenir des données encodées au format JPG. 

		/// <summary>
		/// Compresse et réduit une image JPG
		/// </summary>
		/// <param name="stream">Stream de l'image à compresser</param>
		/// <param name="maxSize">Taille maximun de l'image compressée</param>
		/// <param name="quality">Qualité de compression JGP (0 - 100)</param>
		/// <returns>Tableau de byte contenant l'image compressée</returns>
		public static byte[] CompressImage(Stream stream, Int32 maxSize, Int32 quality)
		{
			BitmapImage imageSource = null;
			Int32 width = 1;
			Int32 height = 1;

			//Création d'un BitmapImage depuis le Stream
			imageSource = new BitmapImage();
			imageSource.CreateOptions = BitmapCreateOptions.DelayCreation;
			imageSource.SetSource(stream);

			//Calcul de la largeur et de la hauteur
			if (imageSource.PixelWidth > imageSource.PixelHeight)
			{
				width = imageSource.PixelWidth * maxSize / imageSource.PixelHeight;
				height = maxSize;
			}
			else
			{
				width = maxSize;
				height = imageSource.PixelHeight * maxSize / imageSource.PixelWidth;
			}

			//Création du WritableBitmap de sortir
			WriteableBitmap w = new WriteableBitmap(width, height);

			//image qui va nous servir générer le thumbnail
			Image i = new Image();
			i.Stretch = Stretch.Uniform;
			i.Source = imageSource;
			i.Height = height;
			i.Width = width;

			//on effectue un rendu de l'image dans le WritableBitmap ! et on obtient l'image miniature !
			w.Render(i, null);
			w.Invalidate();

			//on convertie le WritableBitmap
			return ConvertToBytes(w, quality);
		}

Ensuite il faut convertir le WritableBitmap en raster et compresser celui-ci en flux JPG (entrent en scene la librairie FJCore) et ainsi récupérer les bytes de la miniature ! 


		/// <summary>
		/// Obtient les bytes correspondant à un WritableBitmap dans un format JPG
		/// </summary>
		/// <param name="bitmap">Le bitmap à traduire en bytes</param>
		/// <param name="quality">Qualité de compression JGP (0 - 100)</param>
		/// <returns>Tableau de byte contenant l'image compressée</returns>
		private static byte[] ConvertToBytes(WriteableBitmap bitmap, Int32 quality)
		{
			int width = bitmap.PixelWidth;
			int height = bitmap.PixelHeight;
			int bands = 3;
			byte[] jpgBinaryData = null;
			byte[][,] raster = new byte[bands][,];

			//Génération du raster de l'image
			for (int i = 0; i < bands; i++)
			{
				raster[i] = new byte[width, height];
			}

			for (int row = 0; row < height; row++)
			{
				for (int column = 0; column < width; column++)
				{
					int pixel = bitmap.Pixels[width * row + column];
					raster[0][column, row] = (byte)(pixel >> 16);
					raster[1][column, row] = (byte)(pixel >> 8);
					raster[2][column, row] = (byte)pixel;
				}
			}

			//Creation d'une image FJCore
			FluxJpeg.Core.ColorModel model = new FluxJpeg.Core.ColorModel
			{
				colorspace = FluxJpeg.Core.ColorSpace.RGB
			};

			FluxJpeg.Core.Image img = new FluxJpeg.Core.Image(model, raster);

			//encodage de l'image en JPG et compression de celle ci
			using (MemoryStream stream = new MemoryStream())
			{
				JpegEncoder encoder = new JpegEncoder(img, quality, stream);
				encoder.Encode();

				stream.Seek(0, SeekOrigin.Begin);
				jpgBinaryData = new Byte[stream.Length];
				stream.Read(jpgBinaryData, 0, (int)stream.Length);
			}

			//on renvoie les bytes de l'imgae compressée
			return jpgBinaryData;
		}

Voici le code source complet : 

using System;
using System.Windows.Controls;
using System.Windows.Media;
using System.IO;
using System.Windows.Media.Imaging;
using FluxJpeg.Core.Encoder;

namespace SilverlightThumbnails
{
	public class ImageHelpers
	{

		/// <summary>
		/// Compresse et réduit une image JPG
		/// </summary>
		/// <param name="stream">Stream de l'image à compresser</param>
		/// <param name="maxSize">Taille maximun de l'image compressée</param>
		/// <param name="quality">Qualité de compression JGP (0 - 100)</param>
		/// <returns>Tableau de byte contenant l'image compressée</returns>
		public static byte[] CompressImage(Stream stream, Int32 maxSize, Int32 quality)
		{
			BitmapImage imageSource = null;
			Int32 width = 1;
			Int32 height = 1;

			//Création d'un BitmapImage depuis le Stream
			imageSource = new BitmapImage();
			imageSource.CreateOptions = BitmapCreateOptions.DelayCreation;
			imageSource.SetSource(stream);

			//Calcul de la largeur et de la hauteur
			if (imageSource.PixelWidth > imageSource.PixelHeight)
			{
				width = imageSource.PixelWidth * maxSize / imageSource.PixelHeight;
				height = maxSize;
			}
			else
			{
				width = maxSize;
				height = imageSource.PixelHeight * maxSize / imageSource.PixelWidth;
			}

			//Création du WritableBitmap de sortir
			WriteableBitmap w = new WriteableBitmap(width, height);

			//image qui va nous servir générer le thumbnail
			Image i = new Image();
			i.Stretch = Stretch.Uniform;
			i.Source = imageSource;
			i.Height = height;
			i.Width = width;

			//on effectue un rendu de l'image dans le WritableBitmap ! et on obtient l'image miniature !
			w.Render(i, null);
			w.Invalidate();

			//on convertie le WritableBitmap
			return ConvertToBytes(w, quality);
		}

		/// <summary>
		/// Obtient les bytes correspondant à un WritableBitmap dans un format JPG
		/// </summary>
		/// <param name="bitmap">Le bitmap à traduire en bytes</param>
		/// <param name="quality">Qualité de compression JGP (0 - 100)</param>
		/// <returns>Tableau de byte contenant l'image compressée</returns>
		private static byte[] ConvertToBytes(WriteableBitmap bitmap, Int32 quality)
		{
			int width = bitmap.PixelWidth;
			int height = bitmap.PixelHeight;
			int bands = 3;
			byte[] jpgBinaryData = null;
			byte[][,] raster = new byte[bands][,];

			//Génération du raster de l'image
			for (int i = 0; i < bands; i++)
			{
				raster[i] = new byte[width, height];
			}

			for (int row = 0; row < height; row++)
			{
				for (int column = 0; column < width; column++)
				{
					int pixel = bitmap.Pixels[width * row + column];
					raster[0][column, row] = (byte)(pixel >> 16);
					raster[1][column, row] = (byte)(pixel >> 8);
					raster[2][column, row] = (byte)pixel;
				}
			}

			//Creation d'une image FJCore
			FluxJpeg.Core.ColorModel model = new FluxJpeg.Core.ColorModel
			{
				colorspace = FluxJpeg.Core.ColorSpace.RGB
			};

			FluxJpeg.Core.Image img = new FluxJpeg.Core.Image(model, raster);

			//encodage de l'image en JPG et compression de celle ci
			using (MemoryStream stream = new MemoryStream())
			{
				JpegEncoder encoder = new JpegEncoder(img, quality, stream);
				encoder.Encode();

				stream.Seek(0, SeekOrigin.Begin);
				jpgBinaryData = new Byte[stream.Length];
				stream.Read(jpgBinaryData, 0, (int)stream.Length);
			}

			//on renvoie les bytes de l'imgae compressée
			return jpgBinaryData;
		}

	}
}

Exemple d’utilisation

Code XAML

<UserControl x:Class="SilverlightThumbnails.MainPage"
			 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
			 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
	<Grid x:Name="LayoutRoot">
		<StackPanel HorizontalAlignment="Left"
					VerticalAlignment="Top">
			<Button x:Name="OpenButton"
					Content="Parcourir..."
					Click="OpenClick" />
			<Button x:Name="SaveButton"
					IsEnabled="False"
					Content="Sauvegarder..."
					Click="SaveClick" />
		</StackPanel>
	</Grid>
</UserControl>

Code behind


using System.Windows;
using System.Windows.Controls;
using System.IO;

namespace SilverlightThumbnails
{
	public partial class MainPage : UserControl
	{
		byte[] compressedImage = null;

		public MainPage()
		{
			InitializeComponent();
		}

		private void OpenClick(object sender, RoutedEventArgs e)
		{
			OpenFileDialog ofd = new OpenFileDialog();
			ofd.Filter = "Fichiers image|*.jpg;*.jpeg";
			ofd.ShowDialog();

			compressedImage = null;

			if (ofd.File != null)
			{
				using (Stream imageStream = ofd.File.OpenRead())
				{
					compressedImage = ImageHelpers.CompressImage(imageStream, 400, 90);
				}
			}

			if (compressedImage != null)
			{
				MessageBox.Show("Image compressée !");
				SaveButton.IsEnabled = true;
			}
		}

		private void SaveClick(object sender, RoutedEventArgs e)
		{
			if (compressedImage != null)
			{
				OpenButton.IsEnabled = false;

				SaveFileDialog sfd = new SaveFileDialog();
				sfd.Filter = "Fichiers image|*.jpg;*.jpeg";
				sfd.DefaultExt = ".jpg";

				sfd.ShowDialog();

				using (Stream fs = (Stream)sfd.OpenFile())
				{
					fs.Write(compressedImage, 0, compressedImage.Length);
				}

				SaveButton.IsEnabled = false;
				OpenButton.IsEnabled = true;

				MessageBox.Show("Image enregistrée !");
			}
		}
	}
}

Résultat

 Image originale  Image compressée  Code source complet du tutorial SilverlightThumbnails.zip 

 

Catégories :Silverlight, Silverlight 3

[WPF] : Seesmic desktop

WPF n’est pas mort ! pour preuve voici une nouvelle application développée en WPF fonctionnant sur Windows XP, Vista et 7 (lequel disposant de fonctionnalités additionnelles) :

Cette application, se positionnant comme un client riche de la plateforme Tweeter, à fait l’objet d’une présentation au tout récent PDC09 et a été développée conjointement avec Microsoft dans les locaux de Seesmic

Blog de l’équipe de développement, ici !

Si vous souhaitez l’essayer, c’est par ici !

Catégories :Silverlight, Silverlight 3

[WPF – SL3] : Sobees client

Une nouvelle version de ce client WPF permettant de visionner les réseaux sociaux tels que MySpace, Tweeter ou Facebook à été mise en ligne ! Son nom : Sobees lite

Sachez qu’il existe une version OOB de ce client en Silverlight 3 ! vous pouvez le trouver ici.

Catégories :Silverlight, Silverlight 3, WPF

[WPF – SL3] : Prism october Release !

Une petite actualité pour vous annoncer la sortie officielle de la version 2.1 (octobre 2009) de la Composite Application Guidance (PRISM) pour WPF et Silverlight 3.

pnp_logo

Au menu de cette nouvelle version vous trouverez :

  • une mise à jour des projets afin de cibler Silverlight 3
  • Implémentation de WeakEvents dans les classes DelegateCommand and CompositeCommand, qui corrigent les problèmes de Memory Leak que vous pouviez rencontrer.
  • Amélioration du TabRegionControlAdapter
  • Ainsi que quelques corrections mineures de code.

Sautez vite dessus !

Composite Application Guidance for WPF and Silverlight – October 2009

ou

CodePlex

Catégories :PRISM, Silverlight 3, WPF