.net滚动条自动滚动问题

发布于 2024-09-25 06:18:57 字数 367 浏览 7 评论 0原文

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

如果我现在将窗口大小调整为宽 16 像素(以便我的窗口区域与滚动条出现之前一样宽),滚动条就会消失。现在,如果我将其大小调整回原来的大小,它就会消失。

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

有人知道解决方法吗,或者我正在做一些让 Windows 出错的事情?

I'm writing an app in .net that uses the autoscroll for a layout panel in a dialog.
It seems that whenever I resize the window so that the vertical scrollbars should appear, the horizontal scrollbar automatically appears also. Looking closely at it, the second scrollbar now allows me to scroll the window by 16 pixels (the width of the other scrollbar). So windows seems to think I need a client area that is at least as wide as it was before the vertical scrollbar appeared.

If I now resize the window to be 16 pixels wider (so that my window area is as wide as it was before the scrollbar appeared), the scrollbar goes away. Now if I resize it back down to what it was, it stays away.

So it would appear to me that there is a bug in the system where the minimum width is somehow sticky, but upsizing and downsizging the window (with the mouse, and without tuching the scrollbars related APIs) clears the condition

Does anybody know of a workaround, or am I doing something to trip up Windows?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

请帮我爱他 2024-10-02 06:18:57

这是 Windows 中的一个已知错误 - 此处

解决此问题的最佳方法是将自动调整大小的表格布局面板放入另一个停靠到的面板中主窗体并设置 autoscroll = true

所以您不再使用 tablelayoutpanel 滚动,这是有问题的,您使用面板滚动并且 tablelayoutpanel 位于面板内部

This is a known bug in windows - here

Best way to fix this is to put the table layout panel autosized inside another panel which is docked to the main form and set with autoscroll = true

So you are no longer using the tablelayoutpanel to scroll which is buggy, you use the panel to scroll and the tablelayoutpanel is inside the panel

╰ゝ天使的微笑 2024-10-02 06:18:57

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

为了避免这种影响,布局引擎必须多次遍历布局,处理不断变化的客户区域。然而它只通过了一次。这听起来很明智,这可能是一个潜在的永无止境的循环。我不知道对此有什么好的解决办法。您的用户可能只是将窗口大小调整得足够大,以消除至少一个滚动条。

Yes, I think you already diagnosed the problem correctly. It is a nasty side effect of, say, the vertical scrollbar appearing and needing space, making the available client area smaller. Too small to fit the controls, now also making the horizontal scrollbar appear. It is actually bi-stable, the horizontal bar can flicker on and off in certain cases.

To avoid this effect, the layout engine would have to make multiple passes through the layout, dealing with the changing client area. It however only makes one pass. Which sounds wise, this could be a potentially never ending loop. I don't know of a decent fix for this. Your user will probably just resize the window large enough to get rid of at least one of the scrollbars.

我没有确切地注意到您所描述的行为,但遇到了垂直滚动条的外观使得水平滚动条成为必要的情况。

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

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

HTH

I haven't noticed exactly the behavior you describe, but have run into situations where the appearance of the vertical scrollbar makes a horizontal scrollbar necessary.

You could set the contents of the panel to allow for the width of the scrollbar, for example if I have a ListBox in a Panel:

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

HTH

放飞的风筝 2024-10-02 06:18:57

我刚刚遇到这个问题。我使用的修复方法是将 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 并不总是 true,您可以将重新计算设置为有条件的。

I just encountered this issue. The fix I used was to set Scrollable to false and then to true. Here is an example with a ListView Resize event:

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

If Scrollable is not always true, you can make the recalculation conditional.

彡翼 2024-10-02 06:18:57

尽管这是一个老问题,但它仍然是 .NET 4 中的一个问题。在阅读了有关该问题的尽可能多的内容后,我将解决方案的组合整合到了一个帮助程序类中。

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

我的帮助器类尝试修复的两个“错误”是,第一,从不显示水平滚动条,第二,当垂直滚动条出现时,面板的宽度自动增加以适应它。

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

解决方案

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

最后一点是,在处理 SizeChanged 事件时,您不能执行任何更改面板大小的操作。如果你这样做,就会发生不好的事情。因此,如果我需要因 SizeChanged 事件而更改填充,我会安排稍后进行更改。

无论如何,这是辅助类的代码。它假设您拥有所有适当的“using”语句,包括用于 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;
            }
        }
    }
}

Despite this being an old question, it is still a problem in .NET 4. Having read as much as I could find on the issue, I have rolled a combination of solutions into a helper class.

First, here is the result that I am shooting for ... I have a panel that contains a variety of controls. The child controls, and their sizes, can change based on user activity. I want the panel to resize horizontally so that there is never a horizontal scroll bar, but if there is not enough room vertically, I want the vertical scroll bar to appear. Further, the vertical scroll bar can not cover any of my child controls when it appears, and I don't want to leave a gap for it when it is not needed.

The two 'bugs' that my helper class attempts to fix are first, never show the horizontal scroll bar, and second, when the vertical scroll bar appears, have the panel's width increase automatically to accommodate it.

My assumptions are that the panel is set to AutoSize and AutoScroll, and the child controls are set to AutoSize as well.

The Solution

The helper class attaches itself to the panel (by handling the Paint and SizeChanged events) and does two things. First, it disables the horizontal scroll bar. This is not as easy as it sounds, and I found the solution to this problem here Horizontal scroll bar answer by Kbv Subrahmanyam. Second, in response to the Paint and SizeChanged events, as well as a background timer, it checks to see if the Visible property of the vertical scroll bar has changed. If so, the helper class alters the Right Padding property of the panel to add or remove the extra space the scroll bar requires. The use of the various panel events and the timer are required because .NET exposes no events at all for the scroll bar (a big design flaw IMHO).

Once final point is that you can't do anything that changes the size of the panel while handling the SizeChanged event. Bad Stuff(tm) happens if you do. So, if I need to change the padding due to a SizeChanged event, I schedule that change for later.

Anyway, here is the code for the helper class. It assumes you have all the appropriate 'using' statements, including one for 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;
            }
        }
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文