Скрытие полосы прокрутки при разрешении прокрутки с помощью колесика мыши в FlowLayoutPanel

Я пытаюсь создать динамическую панель, на которую я могу добавлять элементы управления и прокручивать, когда элементы управления находятся за пределами высоты панели, а также скрывать полосы прокрутки.

Я использую FlowLayoutPanel и постоянно добавляю в него пользовательские панели, при этом их Width устанавливается равным Width родительского контейнера.
Я также установил для свойства AutoScroll значение true.

Однако остается одна проблема. Как скрыть проклятые полосы прокрутки? Оба из них.

Я пробовал следующее:

this.lobbiesPanel.AutoScroll = false;
this.lobbiesPanel.HorizontalScroll.Visible = false;
this.lobbiesPanel.VerticalScroll.Visible = false;
this.lobbiesPanel.AutoScroll = true;

К моему разочарованию, это не сработало, как задумано. Полосы прокрутки все еще были видны. Как я могу скрыть полосы прокрутки, сохраняя при этом возможность прокрутки колесиком мыши?

Демонстрация моей проблемы


person yeho    schedule 05.06.2021    source источник
comment
помогает? stackoverflow.com/questions/23911005 /   -  person Ctznkane525    schedule 05.06.2021
comment
Это действительно полезно, но я хочу прокручивать без кнопок. Только колесо мыши.   -  person yeho    schedule 05.06.2021


Ответы (1)


Поскольку вам просто нужно скрыть полосы прокрутки FlowLayoutPanel, а не заменить полосы прокрутки своими собственными элементами управления, вы можете создать пользовательский элемент управления, производный от FlowLayoutPanel.

Пользовательскому элементу управления нужны некоторые функции, которых нет у предка:

  • Его нужно выбрать
  • Должен получать ввод с помощью мыши
  • Он должен иметь возможность прокручиваться, если колесо мыши вращается, когда указатель мыши наводит на дочерний элемент управления, иначе он не будет прокручиваться при заполнении.

Чтобы сделать его доступным для выбора и получать ввод с помощью мыши, вы можете добавить в его конструктор:

SetStyle(ControlStyles.UserMouse | ControlStyles.Selectable, true);

Чтобы заставить его прокручиваться независимо от того, где находится указатель мыши, необходимо предварительно отфильтровать WM_MOUSEWHEEL и, возможно, WM_LBUTTONDOWN сообщений.
Вы можете использовать IMessageFilter Интерфейс для предварительной фильтрации сообщений до их отправки и обработки (это может быть сложно, вы не должны быть жадными и учиться, когда вам нужно отпустить или оставить сообщение для себя).

Когда сообщение WM_MOUSEWHEEL получено и кажется, что оно направлено на ваш элемент управления, вы можете отправить его в FlowLayoutPanel.

Теперь есть хакерская часть: ScrollableControl очень старается показать свои полосы прокрутки, и вы (отчасти) нуждаетесь в них, потому что этот элемент управления имеет очень странный способ вычисления своего PreferredSize (общая площадь элемента управления, занятая дочерними элементами управления). ) и изменяется на основе FlowDirection, плюс нет реального способа управлять стандартными полосами прокрутки: вы избавляетесь от них или прячете их.
Или вы заменяете их собственными разработанными элементами управления, но это уже другой вопрос .

Чтобы скрыть полосы прокрутки, обычным способом является вызов ShowScrollBar.
Параметр int wBar указывает, какую полосу прокрутки следует скрыть/показать.
Параметр bool bShow указывает, следует ли отображать (true) или скрывать (false) эти полосы прокрутки.

  • FlowLayoutPanel пытается отображать свои полосы прокрутки в определенных условиях, поэтому вам нужно перехватывать определенные сообщения и каждый раз вызывать ShowScrollBar (вы не можете просто вызвать эту функцию один раз и забыть о ней).

Вот тестовый пользовательский элемент управления, который реализует все это:
(это рабочий код, но не совсем производственный уровень: вам придется немного поработать над ним, я полагаю , чтобы он вел себя так, как вы предпочитаете в конкретных условиях/вариантах использования)

using System.ComponentModel;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;

[DesignerCategory("code")]
public class FlowLayoutPanelNoScrollbars : FlowLayoutPanel, IMessageFilter
{
    public FlowLayoutPanelNoScrollbars() {
        SetStyle(ControlStyles.UserMouse | ControlStyles.Selectable, true);
    }

    protected override void OnHandleCreated(EventArgs e) {
        base.OnHandleCreated(e);
        Application.AddMessageFilter(this);

        this.VerticalScroll.LargeChange = 60;
        this.VerticalScroll.SmallChange = 20;

        this.HorizontalScroll.LargeChange = 60;
        this.HorizontalScroll.SmallChange = 20;
    }

    protected override void OnHandleDestroyed(EventArgs e) 
    {
        base.OnHandleDestroyed(e);
        Application.RemoveMessageFilter(this);
    }

    protected override void WndProc(ref Message m)
    {
        base.WndProc(ref m);
        switch (m.Msg) {
            case WM_PAINT:
            case WM_ERASEBKGND:
            case WM_NCCALCSIZE:
                if (DesignMode || !AutoScroll) break;
                ShowScrollBar(this.Handle, SB_SHOW_BOTH, false);
                break;
            case WM_MOUSEWHEEL:
                // Handle Mouse Wheel for other specific cases
                int delta = (int)(m.WParam.ToInt64() >> 16);
                int direction = Math.Sign(delta);
                ShowScrollBar(this.Handle, SB_SHOW_BOTH, false); 
                break;
        }
    }

    public bool PreFilterMessage(ref Message m)
    {
        switch (m.Msg) {
            case WM_MOUSEWHEEL:
            case WM_MOUSEHWHEEL:
                if (DesignMode || !AutoScroll) return false;
                if (VerticalScroll.Maximum <= ClientSize.Height) return false;
                // Should also check whether the ForegroundWindow matches the parent Form.
                if (RectangleToScreen(ClientRectangle).Contains(MousePosition)) {
                    SendMessage(this.Handle, WM_MOUSEWHEEL, m.WParam, m.LParam);
                    return true;
                }
                break;
            case WM_LBUTTONDOWN:
                // Pre-handle Left Mouse clicks for all child Controls
                if (RectangleToScreen(ClientRectangle).Contains(MousePosition)) {
                    // Do something here, if needed
                    return true; // <= eventually, with caution
                }
                return false;
        }
        return false;
    }

    private const int WM_PAINT = 0x000F;
    private const int WM_ERASEBKGND = 0x0014;
    private const int WM_NCCALCSIZE = 0x0083;
    private const int WM_LBUTTONDOWN = 0x0201;
    private const int WM_MOUSEWHEEL = 0x020A;
    private const int WM_MOUSEHWHEEL = 0x020E;
    private const int SB_SHOW_VERT = 0x1;
    private const int SB_SHOW_BOTH = 0x3; 

    [DllImport("user32.dll", SetLastError = true)]
    private static extern bool ShowScrollBar(IntPtr hWnd, int wBar, bool bShow);

    [DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    private static extern int SendMessage(IntPtr hWnd, uint uMsg, IntPtr wParam, IntPtr lParam);
}

Вот как это работает:

FlowLayoutPanel без полос прокрутки

person Jimi    schedule 06.06.2021
comment
Работает отлично. У меня только один комментарий. Я изменил аргумент wBar в вызове ShowScrollBar на SB_SHOW_VERT, и он отлично работает! Единственная проблема заключается в том, что когда панель перегружена элементами управления, на мгновение появляется горизонтальная полоса прокрутки, а затем с добавлением другой панели она больше не отображается. Я не использую SB_SHOW_BOTH, потому что это по какой-то причине портит графику панели внутри и ломает некоторые компоненты. Есть ли способ исправить это? В остальном отличный ответ, спасибо! - person yeho; 06.06.2021
comment
imgur.com/a/BVy1JRi - person yeho; 06.06.2021
comment
Привет, повторный вызов ShowScrollBar в вашем редактировании был именно тем, что исправило это. Большое спасибо! Хорошее решение. - person yeho; 06.06.2021