2008-10-06 4 views
2

J'ai un formulaire où les contrôles sont ajoutés dynamiquement à un panneau. Cependant, quand ils le font, ils sont plusieurs fois ajoutés au-dessous du pli (le fond du récipient). Il est agréable que .NET Framework fournisse cette méthode ScrollControlIntoView, cependant, pour plus de facilité d'utilisation, il serait également agréable d'avoir un moyen facile d'animer pour que l'utilisateur puisse facilement comprendre que le panneau a été défilé automatiquement.Existe-t-il un moyen facile d'animer la méthode ScrollableControl.ScrollControlIntoView?

Est-ce que quelqu'un a déjà rencontré cela ou a des idées sur la façon de s'y attaquer?

Répondre

1

Vous pouvez sous-classe Panel, ajoutez un Timer et passer outre ScrollIntoView() afin de faire tout cela dans votre nouveau AnimatedScrollPanel. Je suis sûr que vous devrez le faire de cette façon afin d'utiliser certaines méthodes protected, telles que ScrollToControl() qui renvoie un Point (utilisé par ScrollIntoView).

1

@Joel

Merci! Je l'ai, n'était pas trop difficile à faire avec un peu d'aide de réflecteur. Il pourrait y avoir des façons de l'optimiser, mais c'est ce que je dois commencer. Il se trouve que j'avais besoin de cette fonctionnalité dans un FlowLayoutPanel, mais cela fonctionnerait avec tout ce qui hérite de ScrollableControl.

Modifier: J'ai changé quelques choses à cause de @Joel B Fant en soulignant que je ne supprimais pas le délégué, en gardant l'objet timer indéfiniment. Je crois que j'ai allégé cette préoccupation en affectant le délégué à un objet EventHandler afin qu'il puisse être supprimé en lui-même.

using System; 
using System.Drawing; 
using System.Reflection; 
using System.Windows.Forms; 

public class AnimatedScrollFlowLayoutPanel : FlowLayoutPanel 
{ 
    public new void ScrollControlIntoView(Control activeControl) 
    { 
     if (((this.IsDescendant(activeControl) && this.AutoScroll) && 
      (this.HScroll || this.VScroll)) && (((activeControl != null) && 
      (ClientRectangle.Width > 0)) && (ClientRectangle.Height > 0))) 
     { 
      Point point = this.ScrollToControl(activeControl); 
      int x = DisplayRectangle.X, y = DisplayRectangle.Y; 
      bool scrollUp = x < point.Y; 
      bool scrollLeft = y < point.X; 

      Timer timer = new Timer(); 
      EventHandler tickHandler = null; 
      tickHandler = delegate { 
       int jumpInterval = ClientRectangle.Height/10; 

       if (x != point.X || y != point.Y) 
       { 
        y = scrollUp ? 
         Math.Min(point.Y, y + jumpInterval) : 
         Math.Max(point.Y, y - jumpInterval); 
        x = scrollLeft ? 
         Math.Min(point.X, x + jumpInterval) : 
         Math.Max(point.X, x - jumpInterval); 

        this.SetScrollState(8, false); 
        this.SetDisplayRectLocation(x, y); 
        this.SyncScrollbars(true); 
       } 
       else 
       { 
        timer.Stop(); 
        timer.Tick -= tickHandler; 
       } 
      }; 

      timer.Tick += tickHandler; 
      timer.Interval = 5; 
      timer.Start(); 
     } 
    } 

    internal bool IsDescendant(Control descendant) 
    { 
     MethodInfo isDescendantMethod = typeof(Control).GetMethod(
      "IsDescendant", BindingFlags.NonPublic | BindingFlags.Instance); 
     return (bool)isDescendantMethod.Invoke(this, new object[] { descendant }); 
    } 

    private void SyncScrollbars(bool autoScroll) 
    { 
     MethodInfo syncScrollbarsMethod = typeof(ScrollableControl).GetMethod(
      "SyncScrollbars", BindingFlags.NonPublic | BindingFlags.Instance); 
     syncScrollbarsMethod.Invoke(this, new object[] { autoScroll }); 
    } 
} 
+0

Il y a un gros problème avec ça. Vous continuez d'ajouter le délégué à l'événement Tick et ne le supprimez jamais lorsque vous avez terminé. Vous devez redessiner un peu afin que vous n'ayez que le délégué 1 dans l'événement Tick à la fois. –

+0

Une telle refonte impliquera probablement de ne pas utiliser un délégué anonyme. Vous ne pouvez pas supprimer un délégué anonyme de l'événement. –

+0

Merci pour vos commentaires. Je ne suis pas sûr que je comprends bien si: si je devais changer pour ne pas utiliser un délégué anonyme mon code pour ajouter le délégué serait quelque chose comme timer.Tick + = new EventHandler (timer_Tick) Où me suggérez-vous besoin de retirer le délégué de l'événement? –