我正在构建用于显示切片地图的自定义用户控件,因为我选择ScrollableControl
作为基类,因为我想在我的控件中设置滚动条。
我已经成功创建了绘制逻辑,负责只绘制所需的元素。
现在我尝试添加静态文本,这些文本将始终在同一位置显示(在我的情况下,左上角带有红色文字的白色框):
上面的gif上没有清晰可见,但是当我使用鼠标或滚动条滚动时,白框闪烁并跳了一下。
我的问题是我应该如何更改我的代码,使可滚动内容和固定位置内容位于该可滚动内容之上?
ScrollableControl
作为基类是不错的选择?
以下是我的代码:
class TestControl : ScrollableControl
{
private int _tileWidth = 40;
private int _tileHeight = 40;
private int _tilesX = 20;
private int _tilesY = 20;
public TestControl()
{
SetStyle(ControlStyles.ResizeRedraw, true);
SetStyle(ControlStyles.UserPaint, true);
SetStyle(ControlStyles.AllPaintingInWmPaint, true);
SetStyle(ControlStyles.Opaque, true);
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
UpdateStyles();
ResizeRedraw = true;
AutoScrollMinSize = new Size(_tilesX * _tileWidth, _tilesY * _tileHeight);
Scroll += (sender, args) => { Invalidate(); };
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
e.Graphics.FillRectangle(new SolidBrush(BackColor), ClientRectangle);
e.Graphics.TranslateTransform(AutoScrollPosition.X, AutoScrollPosition.Y);
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
var offsetX = (AutoScrollPosition.X * -1) / _tileWidth;
var offsetY = (AutoScrollPosition.Y * -1) / _tileHeight;
var visibleX = Width / _tileWidth + 2;
var visibleY = Height / _tileHeight + 2;
var x = Math.Min(visibleX + offsetX, _tilesX);
var y = Math.Min(visibleY + offsetY, _tilesY);
for (var i = offsetX; i < x; i++)
{
for (var j = offsetY; j < y; j++)
{
e.Graphics.FillRectangle(Brushes.Beige, new Rectangle(i*_tileWidth, j*_tileHeight, _tileWidth, _tileHeight));
e.Graphics.DrawString(string.Format("{0}:{1}", i, j), Font, Brushes.Black, new Rectangle(i * _tileWidth, j * _tileHeight, _tileWidth, _tileHeight));
}
}
using (var p = new Pen(Color.Black))
{
for (var i = offsetX + 1; i < x; i++)
{
e.Graphics.DrawLine(p, i*_tileWidth, 0, i*_tileWidth, y*_tileHeight);
}
for (var i = offsetY + 1; i < y; i++)
{
e.Graphics.DrawLine(p, 0, i*_tileHeight, x*_tileWidth, i*_tileHeight);
}
}
e.Graphics.FillRectangle(Brushes.White, AutoScrollPosition.X * -1, AutoScrollPosition.Y * -1, 35, 14);
e.Graphics.DrawString("TEST", DefaultFont, new SolidBrush(Color.Red), AutoScrollPosition.X * -1, AutoScrollPosition.Y * -1);
}
}
编辑:
我搜索了一下,找到了具有类似功能的UserControl - https://www.codeproject.com/Articles/16009/A-Much-Easier-to-Use-ListView,在阅读了有关控件的作者博客http://objectlistview.sourceforge.net/cs/blog1.html#blog-overlays之后,我发现他正在使用透明表单位于控制之上。
我真的很想避免这种情况,但仍然可以覆盖我的控制权。
答案 0 :(得分:3)
您正在与名为&#34的Windows系统选项进行战斗;在拖动时显示窗口内容&#34;。默认情况下始终处于开启状态,this web page显示如何将其关闭。
解决问题,但它不是您可以依赖的东西,因为它影响所有应用程序中的所有可滚动窗口。要求用户为您关闭它是不现实的,用户喜欢这个选项,所以他们会忽略你。他们没有提供关闭特定窗口的选项是一个非常重要的疏忽。在自助服务终端应用中,这是一个很好的解决方案。
简而言之,该选项的工作方式是Windows本身使用ScrollWindowEx() winapi函数滚动窗口内容。使用窗口内容的bitblt来移动像素,并仅为滚动显示的窗口部分生成绘制请求。通常只有几行像素,因此完成得非常快。问题是,bitblt也会移动你的固定像素。重画将他们移回去。非常引人注目的是,人眼对这样的运动非常敏感,有助于避免在过去的一百万年中成为狮子午餐。
你必须从ScrollWindowsEx()中剔除它,防止它移动像素,即使你无法阻止它被调用。这需要一个沉重的大锤,LockWindowUpdate()。您可以在this post中找到代码。
using System.Runtime.InteropServices;
...
protected override void OnScroll(ScrollEventArgs e) {
if (e.Type == ScrollEventType.First) {
LockWindowUpdate(this.Handle);
}
else {
LockWindowUpdate(IntPtr.Zero);
this.Update();
if (e.Type != ScrollEventType.Last) LockWindowUpdate(this.Handle);
}
}
[DllImport("user32.dll", SetLastError = true)]
private static extern bool LockWindowUpdate(IntPtr hWnd);
不是那么漂亮,使用单独的Label控件应该开始听起来很有吸引力。
答案 1 :(得分:0)
你可以只为该控件添加标签(在顶部),换句话说 - 你不能将它用作面板吗?