Поскольку вам просто нужно скрыть полосы прокрутки 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);
}
Вот как это работает:
person
Jimi
schedule
06.06.2021