[英]ScrollableControl draw border around entire control
I'm building custom user control that is based on ScrollableControl
. 我正在构建基于
ScrollableControl
自定义用户控件。
Right now I'm trying to add border around my control (similar to border that DataGridView has) 现在,我正在尝试在控件周围添加边框(类似于DataGridView的边框)
I'm able to draw border using: 我可以使用以下方法绘制边框:
e.Graphics.TranslateTransform(AutoScrollPosition.X*-1, AutoScrollPosition.Y*-1);
ControlPaint.DrawBorder(e.Graphics, ClientRectangle, Color.DarkBlue, ButtonBorderStyle.Dashed);
but this draws border around ClientRectangle, not around whole control: 但这会在ClientRectangle周围绘制边框,而不是在整个控件周围绘制边框:
As you can see in the above picture, border isn't surrounding scrollbars as it does in DataGridView. 如上图所示,边框没有像DataGridView中那样环绕滚动条。
Can I draw border around entire control so that scrollbars get included in area surrounded by border? 我可以在整个控件周围绘制边框,以使滚动条包含在边框所包围的区域中吗?
EDIT: 编辑:
Based on Textbox custom onPaint I am able to draw custom border, by overriding WndProc
but I get this weird looking border flickering: 基于Textbox的自定义onPaint,我可以通过覆盖
WndProc
来绘制自定义边框,但是我得到了看起来很奇怪的边框闪烁:
Here is full code I have so far: 这是我到目前为止的完整代码:
internal class TestControl : ScrollableControl
{
private int _tileWidth = 100;
private int _tileHeight = 100;
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);
}
private bool _test = true;
[DefaultValue(true)]
public bool Test
{
get { return _test; }
set
{
if(_test==value) return;
_test = value;
Update();
}
}
[DllImport("user32")]
private static extern IntPtr GetWindowDC(IntPtr hwnd);
struct RECT
{
public int left, top, right, bottom;
}
struct NCCALSIZE_PARAMS
{
public RECT newWindow;
public RECT oldWindow;
public RECT clientWindow;
IntPtr windowPos;
}
int clientPadding = 1;
int actualBorderWidth = 1;
Color borderColor = Color.Black;
protected override void WndProc(ref Message m)
{
//We have to change the clientsize to make room for borders
//if not, the border is limited in how thick it is.
if (m.Msg == 0x83 && _test) //WM_NCCALCSIZE
{
if (m.WParam == IntPtr.Zero)
{
RECT rect = (RECT)Marshal.PtrToStructure(m.LParam, typeof(RECT));
rect.left += clientPadding;
rect.right -= clientPadding;
rect.top += clientPadding;
rect.bottom -= clientPadding;
Marshal.StructureToPtr(rect, m.LParam, false);
}
else
{
NCCALSIZE_PARAMS rects = (NCCALSIZE_PARAMS)Marshal.PtrToStructure(m.LParam, typeof(NCCALSIZE_PARAMS));
rects.newWindow.left += clientPadding;
rects.newWindow.right -= clientPadding;
rects.newWindow.top += clientPadding;
rects.newWindow.bottom -= clientPadding;
Marshal.StructureToPtr(rects, m.LParam, false);
}
}
if (m.Msg == 0x85 && _test) //WM_NCPAINT
{
base.WndProc(ref m);
IntPtr wDC = GetWindowDC(Handle);
using (Graphics g = Graphics.FromHdc(wDC))
{
ControlPaint.DrawBorder(g, new Rectangle(0, 0, Size.Width, Size.Height), borderColor, actualBorderWidth, ButtonBorderStyle.Solid,
borderColor, actualBorderWidth, ButtonBorderStyle.Solid, borderColor, actualBorderWidth, ButtonBorderStyle.Solid,
borderColor, actualBorderWidth, ButtonBorderStyle.Solid);
}
return;
}
base.WndProc(ref m);
}
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 + 10, AutoScrollPosition.Y*-1 + 10, 35, 14);
e.Graphics.DrawString("TEST", DefaultFont, new SolidBrush(Color.Red), AutoScrollPosition.X*-1 + 10, AutoScrollPosition.Y*-1 + 10);
e.Graphics.TranslateTransform(AutoScrollPosition.X*-1, AutoScrollPosition.Y*-1);
ControlPaint.DrawBorder(e.Graphics, ClientRectangle, Color.Red, actualBorderWidth, ButtonBorderStyle.None,
Color.Red, actualBorderWidth, ButtonBorderStyle.None, Color.Red, actualBorderWidth, ButtonBorderStyle.Solid,
Color.Red, actualBorderWidth, ButtonBorderStyle.Solid);
}
protected override void OnScroll(ScrollEventArgs e)
{
if (DesignMode)
{
base.OnScroll(e);
return;
}
if (e.Type == ScrollEventType.First)
{
LockWindowUpdate(Handle);
}
else
{
LockWindowUpdate(IntPtr.Zero);
Update();
if (e.Type != ScrollEventType.Last) LockWindowUpdate(Handle);
}
}
protected override void OnMouseWheel(MouseEventArgs e)
{
if (VScroll && (ModifierKeys & Keys.Shift) == Keys.Shift)
{
VScroll = false;
LockWindowUpdate(Handle);
base.OnMouseWheel(e);
LockWindowUpdate(IntPtr.Zero);
Update();
VScroll = true;
}
else
{
LockWindowUpdate(Handle);
base.OnMouseWheel(e);
LockWindowUpdate(IntPtr.Zero);
Update();
}
}
[DllImport("user32.dll", SetLastError = true)]
private static extern bool LockWindowUpdate(IntPtr hWnd);
}
Can this flickering be fixed? 可以解决此闪烁问题吗?
I was able to solve my problem by overriding CreateParams
: 我可以通过重写
CreateParams
解决我的问题:
protected override CreateParams CreateParams
{
[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= NativeMethods.WS_EX_CONTROLPARENT;
cp.ExStyle &= (~NativeMethods.WS_EX_CLIENTEDGE);
cp.Style &= (~NativeMethods.WS_BORDER);
cp.Style |= NativeMethods.WS_BORDER;
return cp;
}
}
and here is required NativeMethods
class: 这是必需的
NativeMethods
类:
internal static class NativeMethods
{
public const int WS_EX_CONTROLPARENT = 0x00010000;
public const int WS_EX_CLIENTEDGE = 0x00000200;
public const int WS_BORDER = 0x00800000;
}
below is the result: 结果如下:
You can simply derive from UserControl
. 您可以简单地从
UserControl
派生。 It has built-in support for showing scrollbars and also has built-in support for showing borders. 它具有用于显示滚动条的内置支持,还具有用于显示边框的内置支持。
All of built-in features of UserControl
can be added to your control which is derived from ScrollableControl
but with some additional try and error effort or taking look at source code of UserControl
. 可以将
UserControl
所有内置功能添加到从ScrollableControl
派生的ScrollableControl
但是需要进行一些额外的尝试和错误工作,或者着眼于UserControl
源代码 。 But using UserControl
class you can simply have those features. 但是使用
UserControl
类可以简单地拥有这些功能。
Show Border 显示边框
To show border, it's enough to set its BorderStyle
to FixedSingle
to get desired feature: 要显示边框,只需将其
BorderStyle
设置为FixedSingle
即可获得所需的功能:
Show Scrollbars 显示滚动条
To gain scroll feature, it's enough to set its AutoScroll
to true and also set a suitable AutoScrollMinSize
for control. 要获得滚动功能,只需将其
AutoScroll
设置为true,并设置合适的AutoScrollMinSize
进行控制即可。 Then when the width or height of the control is less than width or height of given size, the suitable scrollbar will be shown. 然后,当控件的宽度或高度小于给定大小的宽度或高度时,将显示合适的滚动条。
Custom Border Color 自定义边框颜色
I also suppose you want to have different border color for the control, then it's enough to override WndProc
and handle WM_NCPAINT
and draw custom border for the control like this: 我还假设您希望控件具有不同的边框颜色,那么就足以覆盖
WndProc
并处理WM_NCPAINT
并为控件绘制自定义边框,如下所示:
In above example, I've used the same technique which I used for Changing BorderColor of TextBox with a small change, here I checked if the BorderStyle
equals to FixedSingle
the I drew the border with desired color. 在上面的示例中,我使用了与更改TextBox的BorderColor相同的技术,只是做了很小的改动,在这里,我检查
BorderStyle
等于FixedSingle
,然后绘制了具有所需颜色的边框。
Enable the designer to act like a Parent Control at design time 使设计人员在设计时可以像父控件一样工作
If you want to enable it's designer to be able to drop some controls onto your UserControl
, it's enough to decorate it with [Designer(typeof(ParentControlDesigner))]
. 如果要使设计者能够将某些控件放到
UserControl
,就可以用[Designer(typeof(ParentControlDesigner))]
装饰它。 This way, when you drop your UserControl
on form, it can host other controls like a panel control. 这样,当您将
UserControl
放在窗体上时,它可以承载其他控件,例如面板控件。 If you don't like this feature, just don't decorate it with that attribute and it will use Control
designer by default which doesn't act like a parent control. 如果您不喜欢此功能,请不要使用该属性来装饰它,并且默认情况下它将使用
Control
设计器,该设计器的作用不像父控件。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.