I've created a simple solution with 2 projects. The 1st project (class library) contains a custom control called Container which draws itself with rounded corners. The 2nd project (windows forms) is a test application.
我用2个项目创建了一个简单的解决方案。第一个项目(类库)包含一个名为Container的自定义控件,它使用圆角绘制自己。第二个项目(windows窗体)是一个测试应用程序。
If I add a Container instance to main Form in the 2nd project it shows the rounded corners nicely. Also when I run the 2nd project I can see the Container.
如果我在第二个项目中向主窗体添加一个Container实例,它会很好地显示圆角。另外,当我运行第二个项目时,我可以看到Container。
However when I start moving the form (click and hold the title bar), especially when I move it very fast, all the drawing is messed up, drawn over and over again but not clearing it's surface first...
然而,当我开始移动表格(点击并按住标题栏)时,特别是当我快速移动时,所有的绘图都搞砸了,一遍又一遍地绘制但是先不清除它的表面......
I can call Container1.Refresh() in the Form1.Move event, but I don't want to set this every time because this also means I have to call Container1.Refresh() in the Form1.Resize
event and who knows which other event...
我可以在Form1.Move事件中调用Container1.Refresh(),但我不想每次都设置它,因为这也意味着我必须在Form1.Resize事件中调用Container1.Refresh(),谁知道哪个其他事件...
Is there an event in the Container (control) class itself where I should call Me.Refresh() or Me.Update() or Me.Invalidate() ?
在Container(控件)类本身中是否有一个事件,我应该调用Me.Refresh()或Me.Update()或Me.Invalidate()?
For reference (Form1.vb)
供参考(Form1.vb)
Public Class Form1
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
End Sub
Private Sub Form1_Move(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Move
Me.Container1.Refresh()
End Sub
End Class
for reference (Container.vb):
供参考(Container.vb):
Imports System.Windows.Forms
Imports System.Drawing
Imports System.Drawing.Drawing2D
Public Class Container : Inherits Control
Private _Gp As GraphicsPath
Private Sub Container_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles Me.Paint
Dim r As Rectangle = e.ClipRectangle
Dim gp As New GraphicsPath
Dim cs As Integer = 25 'CornerSize'
r.Inflate(-5, -5)
gp.AddArc(r.X, r.Y, cs, cs, 180, 90)
gp.AddArc(r.X + r.Width - cs, r.Y, cs, cs, 270, 90)
gp.AddArc(r.X + r.Width - cs, r.Y + r.Height - cs, cs, cs, 0, 90)
gp.AddArc(r.X, r.Y + r.Height - cs, cs, cs, 90, 90)
Dim t As Single = cs / 2 + r.Y
gp.AddLine(r.X, r.Y + r.Height - cs, r.X, t)
e.Graphics.SmoothingMode = Drawing.Drawing2D.SmoothingMode.AntiAlias
e.Graphics.DrawPath(Pens.Black, gp)
End Sub
End Class
3 个解决方案
#1
This is your problem:
这是你的问题:
Dim r As Rectangle = e.ClipRectangle
Change it to:
将其更改为:
Dim r As Rectangle = Me.ClientRectangle
#2
It looks to me as though your Container
class isn't painting its entire area - normally a control is responsible for painting its entire rectangle.
它看起来好像你的Container类没有绘制整个区域 - 通常控件负责绘制整个矩形。
In order to have a control that doesn't do this - that has transparent areas (like your rounded corners) - you need to give your control the WS_EX_TRANSPARENT
property. Note that this is a Windows API subject, not a .NET one, so you're heading in the direction of some minor voodoo.
为了让控件不执行此操作 - 具有透明区域(如圆角) - 您需要为控件提供WS_EX_TRANSPARENT属性。请注意,这是一个Windows API主题,而不是.NET主题,所以你正朝着一些小巫术的方向发展。
While it's written in C#, the CodeProject article Making Transparent Controls with C# and .NET 3.5 does seem directly relevant to what you're trying to achieve.
虽然它是用C#编写的,CodeProject文章使用C#和.NET 3.5制作透明控件似乎与您想要实现的内容直接相关。
To quote that article, you first need to override the constructor of your UserControl and configure the background:
要引用该文章,首先需要覆盖UserControl的构造函数并配置背景:
public TranspControl()
{
SetStyle(ControlStyles.SupportsTransparentBackColor, true);
SetStyle(ControlStyles.Opaque, true);
this.BackColor = Color.Transparent;
}
Then, you need to override the CreateParams()
method to set the control style WS_EX_TRANSPARENT
:
然后,您需要覆盖CreateParams()方法以设置控件样式WS_EX_TRANSPARENT:
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x20;
return cp;
}
}
#3
It shouldn't be necessary to force a redraw here (under normal circumstances) since that redraw is automatically forced as soon as your control gets nudged.
没有必要在这里强制重绘(在正常情况下),因为一旦你的控件被轻推就会自动强制重绘。
However, what you need to do is clearing the background of your control before painting anything else: otherwise, your painting operation will mingle with previous painting processes. Just add an
但是,您需要做的是在绘制任何其他内容之前清除控件的背景:否则,您的绘制操作将与之前的绘制过程混合。只需添加一个
e.Graphics.Clear(BackColor)
before your other drawing operations in the Paint
event handler. Also, consider using the OnPaint
method rather than the Paint
event since you subclass the control and don't need to resort to the Paint
event handler.
在Paint事件处理程序中的其他绘图操作之前。此外,考虑使用OnPaint方法而不是Paint事件,因为您对控件进行了子类化,并且不需要求助于Paint事件处理程序。
For the record, Refresh
forces a synchronous redraw which is usually not desired. Rather, use Invalidate
which enqueues the redraw request into the default window message queue.
对于记录,Refresh强制执行通常不需要的同步重绘。而是使用Invalidate将重绘请求排入默认窗口消息队列。
#1
This is your problem:
这是你的问题:
Dim r As Rectangle = e.ClipRectangle
Change it to:
将其更改为:
Dim r As Rectangle = Me.ClientRectangle
#2
It looks to me as though your Container
class isn't painting its entire area - normally a control is responsible for painting its entire rectangle.
它看起来好像你的Container类没有绘制整个区域 - 通常控件负责绘制整个矩形。
In order to have a control that doesn't do this - that has transparent areas (like your rounded corners) - you need to give your control the WS_EX_TRANSPARENT
property. Note that this is a Windows API subject, not a .NET one, so you're heading in the direction of some minor voodoo.
为了让控件不执行此操作 - 具有透明区域(如圆角) - 您需要为控件提供WS_EX_TRANSPARENT属性。请注意,这是一个Windows API主题,而不是.NET主题,所以你正朝着一些小巫术的方向发展。
While it's written in C#, the CodeProject article Making Transparent Controls with C# and .NET 3.5 does seem directly relevant to what you're trying to achieve.
虽然它是用C#编写的,CodeProject文章使用C#和.NET 3.5制作透明控件似乎与您想要实现的内容直接相关。
To quote that article, you first need to override the constructor of your UserControl and configure the background:
要引用该文章,首先需要覆盖UserControl的构造函数并配置背景:
public TranspControl()
{
SetStyle(ControlStyles.SupportsTransparentBackColor, true);
SetStyle(ControlStyles.Opaque, true);
this.BackColor = Color.Transparent;
}
Then, you need to override the CreateParams()
method to set the control style WS_EX_TRANSPARENT
:
然后,您需要覆盖CreateParams()方法以设置控件样式WS_EX_TRANSPARENT:
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x20;
return cp;
}
}
#3
It shouldn't be necessary to force a redraw here (under normal circumstances) since that redraw is automatically forced as soon as your control gets nudged.
没有必要在这里强制重绘(在正常情况下),因为一旦你的控件被轻推就会自动强制重绘。
However, what you need to do is clearing the background of your control before painting anything else: otherwise, your painting operation will mingle with previous painting processes. Just add an
但是,您需要做的是在绘制任何其他内容之前清除控件的背景:否则,您的绘制操作将与之前的绘制过程混合。只需添加一个
e.Graphics.Clear(BackColor)
before your other drawing operations in the Paint
event handler. Also, consider using the OnPaint
method rather than the Paint
event since you subclass the control and don't need to resort to the Paint
event handler.
在Paint事件处理程序中的其他绘图操作之前。此外,考虑使用OnPaint方法而不是Paint事件,因为您对控件进行了子类化,并且不需要求助于Paint事件处理程序。
For the record, Refresh
forces a synchronous redraw which is usually not desired. Rather, use Invalidate
which enqueues the redraw request into the default window message queue.
对于记录,Refresh强制执行通常不需要的同步重绘。而是使用Invalidate将重绘请求排入默认窗口消息队列。