Tre*_*ott 7 c# controls winforms scrollable
我有一个自定义控件,可以放大自定义绘制的文档画布.
我尝试使用AutoScroll,但它没有给出满意的结果.当我将AutoScrollPosition和AutoScrollMinSize背靠背(以任何顺序)设置时,它会强制绘画并在每次变焦时引起抖动.我假设这是因为它修改了两个属性时调用了Update而不是Invalidate.
我现在手动设置HorizontalScroll和VerticalScroll属性,AutoScroll设置为false,因为每次缩放级别或客户端大小更改时:
int canvasWidth = (int)Math.Ceiling(Image.Width * Zoom) + PageMargins.Horizontal;
int canvasHeight = (int)Math.Ceiling(Image.Height * Zoom) + PageMargins.Vertical;
HorizontalScroll.Maximum = canvasWidth;
HorizontalScroll.LargeChange = ClientSize.Width;
VerticalScroll.Maximum = canvasHeight;
VerticalScroll.LargeChange = ClientSize.Height;
if (canvasWidth > ClientSize.Width)
{
HorizontalScroll.Visible = true;
}
else
{
HorizontalScroll.Visible = false;
HorizontalScroll.Value = 0;
}
if (canvasHeight > ClientSize.Height)
{
VerticalScroll.Visible = true;
}
else
{
VerticalScroll.Visible = false;
VerticalScroll.Value = 0;
}
int focusX = (int)Math.Floor((FocusPoint.X * Zoom) + PageMargins.Left);
int focusY = (int)Math.Floor((FocusPoint.Y * Zoom) + PageMargins.Top);
focusX = focusX - ClientSize.Width / 2;
focusY = focusY - ClientSize.Height / 2;
if (focusX < 0)
focusX = 0;
if (focusX > canvasWidth - ClientSize.Width)
focusX = canvasWidth - ClientSize.Width;
if (focusY < 0)
focusY = 0;
if (focusY > canvasHeight - ClientSize.Height)
focusY = canvasHeight - ClientSize.Height;
if (HorizontalScroll.Visible)
HorizontalScroll.Value = focusX;
if (VerticalScroll.Visible)
VerticalScroll.Value = focusY;
Run Code Online (Sandbox Code Playgroud)
在这种情况下,FocusPoint是一个PointF结构,它保存用户所关注的位图中的坐标(例如,当他们将鼠标滚轮放大时,他们将焦点放在当时的当前鼠标位置).此功能在很大程度上起作用.
什么不起作用是滚动条.如果用户尝试通过单击任一滚动条手动滚动,它们都会继续返回0.我不会在代码中的任何其他位置设置它们.我尝试在OnScroll()方法中编写以下内容:
if (se.ScrollOrientation == ScrollOrientation.VerticalScroll)
{
VerticalScroll.Value = se.NewValue;
}
else
{
HorizontalScroll.Value = se.NewValue;
}
Invalidate();
Run Code Online (Sandbox Code Playgroud)
但这会导致一些非常不稳定的行为,包括轻弹和滚动越界.
我该怎么写OnScroll的代码? 我已经尝试了base.OnScroll但是当AutoScroll设置为false时它没有做任何事情.
我最终通过创建 3 个子控件来实现我自己的自定义滚动:一个 HScrollBar、一个 VScrollBar 和一个 Panel。
我像这样隐藏 ClientSize 和 ClientRectangle :
public new Rectangle ClientRectangle
{
get
{
return new Rectangle(new Point(0, 0), ClientSize);
}
}
public new Size ClientSize
{
get
{
return new Size(
base.ClientSize.Width - VScrollBar.Width,
base.ClientSize.Height - HScrollBar.Height
);
}
}
Run Code Online (Sandbox Code Playgroud)
布局是在OnClientSizeChanged中完成的:
protected override void OnClientSizeChanged(EventArgs e)
{
base.OnClientSizeChanged(e);
HScrollBar.Location = new Point(0, base.ClientSize.Height - HScrollBar.Height);
HScrollBar.Width = base.ClientSize.Width - VScrollBar.Width;
VScrollBar.Location = new Point(base.ClientSize.Width - VScrollBar.Width, 0);
VScrollBar.Height = base.ClientSize.Height - HScrollBar.Height;
cornerPanel.Size = new Size(VScrollBar.Width, HScrollBar.Height);
cornerPanel.Location = new Point(base.ClientSize.Width - cornerPanel.Width, base.ClientSize.Height - cornerPanel.Height);
}
Run Code Online (Sandbox Code Playgroud)
每个 ScrollBar 的Scroll事件都订阅了以下内容:
private void ScrollBar_Scroll(object sender, ScrollEventArgs e)
{
OnScroll(e);
}
Run Code Online (Sandbox Code Playgroud)
最后,我们可以允许 MouseWheel 事件通过以下方式滚动:
protected override void OnMouseWheel(MouseEventArgs e)
{
int xOldValue = VScrollBar.Value;
if (e.Delta > 0)
{
VScrollBar.Value = (int)Math.Max(VScrollBar.Value - (VScrollBar.SmallChange * e.Delta), 0);
OnScroll(new ScrollEventArgs(ScrollEventType.ThumbPosition, xOldValue, VScrollBar.Value, ScrollOrientation.VerticalScroll));
}
else
{
VScrollBar.Value = (int)Math.Min(VScrollBar.Value - (VScrollBar.SmallChange * e.Delta), VScrollBar.Maximum - (VScrollBar.LargeChange - 1));
OnScroll(new ScrollEventArgs(ScrollEventType.ThumbPosition, xOldValue, VScrollBar.Value, ScrollOrientation.VerticalScroll));
}
}
Run Code Online (Sandbox Code Playgroud)
对于自定义绘画,您可以使用以下语句:
e.Graphics.TranslateTransform(-HScrollBar.Value, -VScrollBar.Value);
Run Code Online (Sandbox Code Playgroud)
这工作得很好,没有使用自动滚动时出现的故障。