【问题标题】:Is there an easy way to animate the ScrollableControl.ScrollControlIntoView method?有没有一种简单的方法来为 ScrollableControl.ScrollControlIntoView 方法设置动画?
【发布时间】:2008-10-06 19:08:47
【问题描述】:

我有一个表单,其中控件被动态添加到面板中。但是,当它们这样做时,它们会多次添加到折叠下方(容器底部)。很高兴 .NET Framework 提供了这个 ScrollControlIntoView 方法,但是,为了增加可用性,如果有一种简单的动画方法让用户很容易理解 Panel 是自动滚动的,那就太好了。

有没有人遇到过这种情况或对如何解决有任何想法?

【问题讨论】:

    标签: c# .net vb.net winforms


    【解决方案1】:

    您可以继承Panel,添加Timer,并覆盖ScrollIntoView(),以便在您的新AnimatedScrollPanel 中完成这一切。我很确定您必须这样做才能使用某些protected 方法,例如返回PointScrollToControl()(由ScrollIntoView 使用)。

    【讨论】:

      【解决方案2】:

      @乔尔

      谢谢!我明白了,在反射器的帮助下,这并不难。可能有优化它的方法,但这是我必须开始的。碰巧我需要在 FlowLayoutPanel 中使用此功能,但它适用于从 ScrollableControl 继承的任何内容。

      编辑:由于@Joel B Fant 指出我没有删除委托,我更改了一些内容,而是无限期地保留计时器对象。我相信我已经通过将委托分配给一个 EventHandler 对象来减轻这种担忧,这样它就可以在自身内部被删除。

      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 });
          }
      }
      

      【讨论】:

      • 这有一个大问题。您不断将委托添加到 Tick 事件中,并且在完成后永远不会将其删除。您需要重新设计一点,以便在 Tick 事件中一次只有 1 个委托。
      • 这样的重新设计可能会涉及不使用匿名委托。您不能从活动中删除匿名代表。
      • 感谢您的 cmets。不过我不确定我是否完全理解:如果我改为不使用匿名委托,我添加委托的代码将类似于 timer.Tick += new EventHandler(timer_Tick) 你建议我在哪里删除活动代表?
      • 我想我会创建一个处理动画的嵌套类。在创建时为其提供对面板实例的引用,以便它可以调用受保护的方法。它有定时器、处理程序、参数。 IDisposable,带有 Finished 事件来调用它。在那和 Dispose() 中,摆脱所有引用。开火即忘。
      猜你喜欢
      • 2015-04-15
      • 1970-01-01
      • 2015-04-28
      • 2012-02-25
      • 2015-08-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多