2022-05-13 00:21:41 -06:00

147 lines
3.8 KiB
C#

using System;
using System.Drawing;
using System.Windows.Forms;
namespace LibationWinForms.ProcessQueue
{
internal delegate void RequestDataDelegate(int firstIndex, int numVisible, ProcessBookControl[] panelsToFill);
internal partial class VirtualFlowControl : UserControl
{
public event RequestDataDelegate RequestData;
public int VirtualControlCount
{
get => _virtualControlCount;
set
{
if (_virtualControlCount == 0)
vScrollBar1.Value = 0;
_virtualControlCount = value;
AdjustScrollBar();
DoVirtualScroll();
}
}
private int _virtualControlCount;
private int VirtualHeight => _virtualControlCount * CONTROL_HEIGHT - vScrollBar1.Height;
private readonly int PROCESSBOOKCONTROL_MARGIN;
private readonly int CONTROL_HEIGHT;
private const int WM_MOUSEWHEEL = 522;
private const int NUM_ACTUAL_CONTROLS = 20;
private const int SCROLL_SMALL_CHANGE = 120;
private const int SCROLL_LARGE_CHANGE = 3 * SCROLL_SMALL_CHANGE;
private readonly VScrollBar vScrollBar1;
private readonly ProcessBookControl[] BookControls = new ProcessBookControl[NUM_ACTUAL_CONTROLS];
public VirtualFlowControl()
{
InitializeComponent();
vScrollBar1 = new VScrollBar
{
Minimum = 0,
Value = 0,
SmallChange = SCROLL_SMALL_CHANGE,
LargeChange = SCROLL_LARGE_CHANGE,
Dock = DockStyle.Right
};
Controls.Add(vScrollBar1);
panel1.Resize += (_, _) => AdjustScrollBar();
if (this.DesignMode)
return;
vScrollBar1.Scroll += (_, _) => DoVirtualScroll();
for (int i = 0; i < NUM_ACTUAL_CONTROLS; i++)
{
BookControls[i] = new ProcessBookControl();
if (i == 0)
{
PROCESSBOOKCONTROL_MARGIN = BookControls[i].Margin.Left + BookControls[i].Margin.Right;
CONTROL_HEIGHT = BookControls[i].Height + BookControls[i].Margin.Top + BookControls[i].Margin.Bottom;
}
BookControls[i].Location = new Point(2, CONTROL_HEIGHT * i);
BookControls[i].Width = panel1.ClientRectangle.Width - PROCESSBOOKCONTROL_MARGIN;
BookControls[i].Anchor = AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Top;
panel1.Controls.Add(BookControls[i]);
}
panel1.Height += SCROLL_SMALL_CHANGE;
}
private void AdjustScrollBar()
{
int maxFullVisible = DisplayRectangle.Height / CONTROL_HEIGHT;
if (VirtualControlCount <= maxFullVisible)
{
vScrollBar1.Enabled = false;
for (int i = VirtualControlCount; i < NUM_ACTUAL_CONTROLS; i++)
BookControls[i].Visible = false;
}
else
{
vScrollBar1.Enabled = true;
//https://stackoverflow.com/a/2882878/3335599
vScrollBar1.Maximum = VirtualHeight + vScrollBar1.LargeChange - 1;
}
}
private void DoVirtualScroll()
{
//https://stackoverflow.com/a/2882878/3335599
int scrollValue = Math.Max(Math.Min(VirtualHeight, vScrollBar1.Value), 0);
int position = scrollValue % CONTROL_HEIGHT;
panel1.Location = new Point(0, -position);
int firstVisible = scrollValue / CONTROL_HEIGHT;
int window = DisplayRectangle.Height;
int count = window / CONTROL_HEIGHT;
if (window % CONTROL_HEIGHT != 0)
count++;
count = Math.Min(count, VirtualControlCount);
RequestData?.Invoke(firstVisible, count, BookControls);
for (int i = 0; i < BookControls.Length; i++)
BookControls[i].Visible = i <= count && VirtualControlCount > 0;
}
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_MOUSEWHEEL)
{
//https://docs.microsoft.com/en-us/windows/win32/inputdev/wm-mousewheel
int wheelDelta = -(short)(((ulong)m.WParam) >> 16 & 0xffff);
if (wheelDelta > 0)
vScrollBar1.Value = Math.Min(vScrollBar1.Value + wheelDelta, vScrollBar1.Maximum);
else
vScrollBar1.Value = Math.Max(vScrollBar1.Value + wheelDelta, vScrollBar1.Minimum);
DoVirtualScroll();
}
base.WndProc(ref m);
}
}
}