DonutLoader [Custom Control]

Aucun commentaire


    Pour les besoins d'une production, nous avons eu besoin d'un visuel d'attente (Torus ou Donut Loader) très proche de ce qui se fait déjà déjà sous Windows. Pour cela, j'ai étendu Control puis j'ai ajouté quelques Dependency properties :

    #region Dps
     
    /// 
    /// Set the speed of animation based on
    /// 
    [Category("Common Properties")]
    [Description("Allow to define animation's duration")]
    public TimeSpan Duration
    {
        get { return (TimeSpan)GetValue(DurationProperty); }
        set { SetValue(DurationProperty, value); }
    }
     
    // Using a DependencyProperty as the backing store for SpeedRatio.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty DurationProperty =
        DependencyProperty.Register("Duration", typeof(TimeSpan), typeof(DonutLoader), new PropertyMetadata(TimeSpan.FromSeconds(1), new PropertyChangedCallback(OnDurationChanged)));
     
    private static void OnDurationChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var donutLoader = d as DonutLoader;
     
        if (d != null)
        {
            ClockState cs = donutLoader.boucle.GetCurrentState()
     
            donutLoader.boucle.Stop();
     
            donutLoader.daBoucle.Duration = (TimeSpan)e.NewValue;
     
            if (cs == ClockState.Active)
            {
                donutLoader.boucle.Begin();
            }
        }
    }
     
    /// 
    /// Percent of progression
    /// 
    [Category("Common Properties")]
    [Description("Current progression value")]
    public double Percent
    {
        get { return (double)GetValue(PercentProperty); }
        set { SetValue(PercentProperty, value); }
    }
     
    // Using a DependencyProperty as the backing store for Percent.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty PercentProperty =
        DependencyProperty.Register("Percent", typeof(double), typeof(DonutLoader), new PropertyMetadata(0d));
     
    /// 
    /// Allow to  hide percent textBlock
    /// 
    [Category("Common Properties")]
    [Description("Show/Hide progression value")]
    public Visibility PercentVisibility
    {
        get { return (Visibility)GetValue(PercentVisibilityProperty); }
        set { SetValue(PercentVisibilityProperty, value); }
    }
     
    // Using a DependencyProperty as the backing store for PercentVisibility.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty PercentVisibilityProperty =
        DependencyProperty.Register("PercentVisibility", typeof(Visibility), typeof(DonutLoader), new PropertyMetadata(Visibility.Visible));
     
    #endregion

    A ce stade, tout fonctionne comme prévu, mais un petit bug m'a pris un peu de temps à résoudre dans les lignes suivantes.

    private void SetOpacityMask()
    {
    BitmapImage bi = new BitmapImage() { UriSource = new Uri("/Tweened.Controls;component/bitmaps/ProgressBitmap.png", UriKind.RelativeOrAbsolute) };
     
         ImageBrush ib = new ImageBrush();
     
         ib.ImageSource = bi;
     
         _DonutFrameworkElement.RenderTransform = new CompositeTransform() { CenterX = .5, CenterY = .5 };
     
         _DonutFrameworkElement.OpacityMask = ib;
    }
     
    private void SetStoryboard()
    {
         boucle.Children.Add(daBoucle);
     
         Storyboard.SetTargetProperty(daBoucle, new PropertyPath("(UIElement.RenderTransform).(CompositeTransform.Rotation)"));
     
         daBoucle.From = 0;
     
         daBoucle.To = -360;
     
         daBoucle.RepeatBehavior = RepeatBehavior.Forever;
     
         daBoucle.Duration = Duration;
    }

    Lorsque l'on joue l'animation de rotation par C# ou XAML, l'objet qui subit l'animation disparaît soudainement au runtime. Après avoir passé un peu de temps sur ce bug, j'ai décidé non pas d'animer l'objet lui même mais son remplissage :

    private void SetOpacityMask()
    {
        BitmapImage bi = new BitmapImage() { UriSource = new Uri("/Tweened.Controls;component/bitmaps/ProgressBitmap.png", UriKind.RelativeOrAbsolute) };
     
        ImageBrush ib = new ImageBrush();
     
        ib.ImageSource = bi;
     
        ib.RelativeTransform = new CompositeTransform() { CenterX = .5, CenterY = .5 };
     
        //ancienne version
        //_DonutFrameworkElement.RenderTransform = new CompositeTransform() { CenterX = .5, CenterY = .5 };
     
        _DonutFrameworkElement.OpacityMask = ib;
    }
     
    private void SetStoryboard()
    {
        boucle.Children.Add(daBoucle);
     
        Storyboard.SetTargetProperty(daBoucle, new PropertyPath("(UIElement.OpacityMask).(Brush.RelativeTransform).(CompositeTransform.Rotation)"));
     
        //ancienne version
        //Storyboard.SetTargetProperty(daBoucle, new PropertyPath("(UIElement.RenderTransform).(CompositeTransform.Rotation)"));
     
        daBoucle.From = 0;
     
        daBoucle.To = -360;
     
        daBoucle.RepeatBehavior = RepeatBehavior.Forever;
     
        daBoucle.Duration = Duration;
    }

    j'ai donc remplacé la ligne ciblant la propriété à animer :

    Storyboard.SetTargetProperty(daBoucle, new PropertyPath("(UIElement.RenderTransform).(CompositeTransform.Rotation)"));

    par :

    Storyboard.SetTargetProperty(daBoucle, new PropertyPath("(UIElement.OpacityMask).(Brush.RelativeTransform).(CompositeTransform.Rotation)"));

    Là, au miracle, plus aucun bug, tout fonctionne parfaitement. C'est la première fois que j'anime, au sein d'un custom contrôle, un objet avec une image en masque d'opacité et j'ai bien perdu 30 minutes là dessus. Si quelqu'un a une idée du pourquoi de ce bug je suis preneur.

    Bookmark and Share

    Leave a Reply

    Security Code: