.net滚动条自动滚动问题

时间:2010-09-27 15:32:04

标签: c# .net resize autoscroll

我正在.net中编写一个应用程序,它在对话框中使用autoscroll作为布局面板。 似乎每当我调整窗口大小以便显示垂直滚动条时,水平滚动条也会自动出现。仔细观察它,第二个滚动条现在允许我将窗口滚动16个像素(另一个滚动条的宽度)。所以Windows似乎认为我需要一个至少与垂直滚动条出现之前一样宽的客户区。

如果我现在将窗口调整为宽16像素(使我的窗口区域与滚动条出现之前一样宽),滚动条就会消失。现在,如果我将其重新调整为原来的状态,它就会消失。

所以在我看来,系统中存在一个错误,其中最小宽度有点粘,但是放大和缩小窗口(使用鼠标,而不调整滚动条相关的API)会清除条件

有人知道某种解决方法,或者我是否正在做一些绊倒Windows的事情?

5 个答案:

答案 0 :(得分:5)

是的,我认为你已经正确诊断出了这个问题。这是一个令人讨厌的副作用,例如,垂直滚动条出现并需要空间,使可用的客户区域更小。太小而不适合控件,现在也会出现水平滚动条。它实际上是双稳态的,在某些情况下水平条可以闪烁。

为了避免这种影响,布局引擎必须在布局中进行多次传递,处理不断变化的客户区域。然而它只通过一次。这听起来很明智,这可能是一个永无止境的循环。我不知道对此有什么好处。您的用户可能只需调整窗口大小,以便至少删除其中一个滚动条。

答案 1 :(得分:5)

这是Windows中的已知错误 - here

解决此问题的最佳方法是将表格布局面板自动调整到另一个面板中,该面板停靠在主窗体上并使用autoscroll = true进行设置

因此,您不再使用tablelayoutpanel滚动哪个有问题,您可以使用面板滚动并且tablelayoutpanel位于面板内

答案 2 :(得分:2)

我没有完全注意到您描述的行为,但遇到了垂直滚动条的外观需要水平滚动条的情况。

您可以设置面板的内容以允许滚动条的宽度,例如,如果ListBox中有Panel

listBox1.Width = panel2.Width - System.Windows.Forms.SystemInformation.VerticalScrollBarWidth;

HTH

答案 3 :(得分:0)

我刚遇到这个问题。我使用的修复方法是将Scrollable设置为false,然后设置为true。以下是ListView Resize事件的示例:

private void myListView_Resize(object sender, EventArgs e)
{
 this.SuspendLayout();

 //Code to do various resizing stuff

 //Force Scrollbar Recalculation
 myListView.Scrollable = false;
 myListView.Scrollable = true;
 this.ResumeLayout(false);
 this.PerformLayout();
}

如果Scrollable并非总是如此,则可以重新计算条件。

答案 4 :(得分:0)

尽管这是一个老问题,但它仍然是.NET 4中的一个问题。尽管我已经在这个问题上找到了尽可能多的内容,但我已经将一组解决方案组合成了一个帮助类。

首先,这是我拍摄的结果......我有一个包含各种控件的面板。子控件及其大小可以根据用户活动进行更改。我希望面板水平调整大小,以便永远不会有水平滚动条,但如果没有足够的垂直空间,我希望显示垂直滚动条。此外,垂直滚动条在出现时无法覆盖我的任何儿童控件,并且在不需要时我不想为它留下空隙。

这两个错误'我的助手类试图修复的是第一个,从不显示水平滚动条,其次,当出现垂直滚动条时,面板的宽度会自动增加以适应它。

我的假设是面板设置为AutoSize和AutoScroll,子控件也设置为AutoSize。

解决方案

辅助类将自身附加到面板(通过处理Paint和SizeChanged事件)并执行两项操作。首先,它禁用水平滚动条。这并不像听起来那么容易,我在这里找到了解决这个问题的方法Horizontal scroll bar answer by Kbv Subrahmanyam。其次,为了响应Paint和SizeChanged事件以及后台计时器,它会检查垂直滚动条的Visible属性是否已更改。如果是这样,辅助类会改变面板的右边距属性,以添加或删除滚动条所需的额外空间。需要使用各种面板事件和计时器,因为.NET为滚动条公开了 no 事件(一个很大的设计缺陷恕我直言)。

最后一点是,在处理SizeChanged事件时,您无法执行任何更改面板大小的操作。如果你这样做,就会发生Bad Stuff(tm)。因此,如果我需要更改由于SizeChanged事件而导致的填充,我会安排更改以供日后使用。

无论如何,这是帮助类的代码。它假定您拥有所有适当的'使用'语句,包括System.Threading ...

/// <summary>
/// This class is intended to beat the AutoSize and AutoScroll features into submission!
/// 
/// Or, at least getting them to work the way I want them to (which may not be the way 
/// others think they should work).
/// 
/// This class will force a panel that has AutoSize enabled to actually increase its
/// width as appropriate when the AutoScroll Vertical scroll bar becomes visible.
/// I like this better than attempting to 'reserve' space for the Vertical scroll bar,
/// which wastes space when the scroll bar is not needed, and leaves ugly gaps in
/// your user interface.
/// </summary>
public class AutoScrollFixer
{
    /// <summary>
    /// This is the panel we are 'fixing'
    /// </summary>
    private Panel _panel;

    /// <summary>
    /// This field keeps track of the original value for
    /// the right padding property of the panel.
    /// </summary>
    private int _originalRightPadding = 0;

    /// <summary>
    /// We use this flag to prevent recursion problems.
    /// </summary>
    private bool _adjusting = false;

    /// <summary>
    /// This flag keeps track of the last known state of the scroll bar.
    /// </summary>
    private bool _lastScrollBarVisible = false;

    /// <summary>
    /// We use a timer to check the scroll bar state every so often.
    /// This is necessary since .NET (in another stunning piece of
    /// architecture from Microsoft) provides absolutely no events
    /// attached to the scroll bars of a panel.
    /// </summary>
    private System.Windows.Forms.Timer _timer = new System.Windows.Forms.Timer();

    /// <summary>
    /// Construct an AutoScrollFixer and attach it to the provided panel.
    /// Once created, there is no particular reason to keep a reference 
    /// to the AutoScrollFixer in your code.  It will silently do its thing
    /// in the background.
    /// </summary>
    /// <param name="panel"></param>
    public AutoScrollFixer(Panel panel)
    {
        _panel = panel;
        _originalRightPadding = panel.Padding.Right;

        EnableVerticalAutoscroll(_panel);
        _lastScrollBarVisible = _panel.VerticalScroll.Visible;

        _panel.Paint += (s, a) =>
        {
            AdjustForVerticalScrollbar();
        };

        _panel.SizeChanged += (s, a) =>
        {
            //
            //  We can't do something that changes the size while handling
            //  a size change.  So, if an adjustment is needed, we will
            //  schedule it for later.
            //
            if (_lastScrollBarVisible != _panel.VerticalScroll.Visible)
            {
                AdjustLater();
            }
        };

        _timer.Tick += (s, a) =>
        {
            //
            //  Sadly, the combination of the Paint event and the SizeChanged event
            //  is NOT enough to guarantee that we will catch a change in the
            //  scroll bar status.  So, as a last ditch effort, we will check
            //  for a status change every 500 mSecs.  Yup, this is a hack!
            //
            AdjustForVerticalScrollbar();
        };

        _timer.Interval = 500;
        _timer.Start();
    }


    /// <summary>
    /// Enables AutoScroll, but without the Horizontal Scroll bar.
    /// Only the Vertical Scroll bar will become visible when necessary
    /// 
    /// This method is based on this StackOverflow answer ...
    /// https://stackoverflow.com/a/28583501/2175233
    /// </summary>
    /// <param name="panel"></param>
    public static void EnableVerticalAutoscroll( Panel panel )
    {
        panel.AutoScroll = false;
        panel.HorizontalScroll.Enabled = false;
        panel.HorizontalScroll.Visible = false;
        panel.HorizontalScroll.Maximum = 0;
        panel.AutoScroll = true;
    }


    /// <summary>
    /// Queue AdjustForVerticalScrollbar to run on the GUI thread after the current
    /// event has been handled.
    /// </summary>
    private void AdjustLater()
    {
        ThreadPool.QueueUserWorkItem((t) => 
        {
            Thread.Sleep(200);
            _panel.BeginInvoke((Action)(() =>
            {
                AdjustForVerticalScrollbar();
            }));
        });
    }


    /// <summary>
    /// This is where the real work gets done.  When this method is called, we will
    /// simply set the right side padding on the panel to make room for the
    /// scroll bar if it is being displayed, or reset the padding value to 
    /// its original value if not.
    /// </summary>
    private void AdjustForVerticalScrollbar()
    {
        if (!_adjusting)
        {
            try
            {
                _adjusting = true;

                if (_lastScrollBarVisible != _panel.VerticalScroll.Visible)
                {
                    _lastScrollBarVisible = _panel.VerticalScroll.Visible;

                    Padding p = _panel.Padding;
                    p.Right = _lastScrollBarVisible ? _originalRightPadding + System.Windows.Forms.SystemInformation.VerticalScrollBarWidth + 2 : _originalRightPadding;
                    _panel.Padding = p;
                    _panel.PerformLayout();
                }
            }

            finally
            {
                _adjusting = false;
            }
        }
    }
}