I'm building custom user control that will be used to display tiles map, as base class I've chosen ScrollableControl
, because I want to have scrollbars in my control.
I've successfully created paint logic that is responsible for painting only needed elements.
Now I'm trying to add static text that will be always visible in same place (in my case white box with red text in top left corner):
This isn't clearly visible on above gif, but that white box blinks and jumps a bit when I scroll using mouse or scrollbars.
My question is how should I change my code to have scrollable content and fixed position content on top of that scrollable content?
Is ScrollableControl
good choice as base class?
Below is my code:
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);
}
}
EDIT:
I've searched a bit and found UserControl that has similar functionality - https://www.codeproject.com/Articles/16009/A-Much-Easier-to-Use-ListView and after reading a bit more on control's author blog http://objectlistview.sourceforge.net/cs/blog1.html#blog-overlays I found out that he is using Transparent Form that is positioned on top of control.
I really would like to avoid that, but still have overlay on top of my control.