Help Repainting a Line
I am doing a custom control (inherited from VisualBasic.PowerPacks.LineShape
), that should be painted like as standard one, but also having a Icon displayed near it.
So, I just overrided OnPaint
like this:
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
e.Graphics.DrawIcon(myIcon, StartPoint.X, StartPoint.Y);
base.OnPaint(e);
}
Now, everything is OK, but when my control moves, the icon still remains drawn on the ancient place.
Is there a way to paint it properly?
alt text http://lh4.ggpht.com/_1TPOP7DzY1E/S5gXmp7xYiI/AAAAAAAADHI/pa1OhpKYSoM/Untitled-2.png Real project situation
CODE: The sample code for tests
alt text http://lh6.ggpht.com/_1TPOP7DzY1E/S5jSluxvtDI/AAAAAAAADHw/EUz0Tfet-rw/s800/Capture2.png
using Microsoft.VisualBasic.PowerPacks;
using System.Windows.Forms;
using System.Drawing;
namespace LineShapeTest
{
///
/// Test Form
///
public class Form1 : Form
{
IconLineShape myLine = new IconLineShape();
ShapeContainer shapeContainer1 = new ShapeContainer();
Panel panel1 = new Panel();
public Form1()
{
this.panel1.Dock = DockStyle.Fill;
// load your back image here
this.panel1.BackgroundImage =
global::WindowsApplication22.Properties.Resources._13820t;
this.panel1.Controls.Add(shapeContainer1);
this.myLine.StartPoint = new Point(20, 30);
this.myLine.EndPoint = new Point(80, 120);
this.myLine.Parent = this.shapeContainer1;
MouseEventHandler panelMouseMove =
new MouseEventHandler(this.panel1_MouseMove);
this.panel1.MouseMove += panelMouseMove;
this.shapeContainer1.MouseMove += panelMouseMove;
this.Controls.Add(panel1);
}
private void panel1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
myLine.StartPoint = e.Location;
}
}
}
///
/// Test LineShape
///
public class IconLineShape : LineShape
{
Icon myIcon = SystemIcons.Exclamation;
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
e.Graphics.DrawIcon(myIcon, StartPoint.X, StartPoint.Y);
base.OnPaint(e);
}
}
}
Nota Bene, for the lineShape:
Parent = ShapeContainer
Parent.Parent = Panel
Update 1 TRACES
In this variant of OnPaint, we have traces:
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
Graphics g = Parent.Parent.CreateGraphics();
g.DrawIcon(myIcon, StartPoint.X, StartPoint.Y);
base.OnPaint(e);
}
alt text http://lh4.ggpht.com/_1TPOP7DzY1E/S5j29lutQ0I/AAAAAAAADH4/4yEnZd_hPjA/s800/Capture3.png
Update 2 BLINKS
In this variant of OnPaint, we have a blinking image:
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
Parent.Parent.Invalidate(this.Region, true);
Graphics g = Parent.Parent.CreateGraphics();
g.DrawIcon(myIcon, StartPoint.X, StartPoint.Y);
base.OnPaint(e);
}
alt text http://lh5.ggpht.com/_1TPOP7DzY1E/S5j4Bam7hiI/AAAAAAAADIA/1hQWKyV8Fr0/s800/Capture4.png
Update 3: External Invalidation
This variant works well, but... from exterior of IconLineShape class:
private void panel1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
Region r = myLine.Region;
myLine.StartPoint = e.Location;
panel1.Invalidate(r);
}
}
///
/// Test LineShape
///
public class IconLineShape : LineShape
{
Icon myIcon = SystemIcons.Exclamation;
Graphics parentGraphics;
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
parentGraphics.DrawIcon(myIcon, StartPoint.X, StartPoint.Y);
base.OnPaint(e);
}
protected override void OnParentChanged(System.EventArgs e)
{
// Parent is a ShapeContainer
// Parent.Parent is a Panel
parentGraphics = Parent.Parent.CreateGraphics开发者_如何学JAVA();
base.OnParentChanged(e);
}
}
Even this resolves the problem of the test example, I need this control to be done inside the control, because I can't force the external "clients" of this control do not forget to save the old region and invalidate the parent each time changing a location...
Did you try clearing the buffer (draw a filling rectangle with background color)? Also make sure to reset clipping regions to the size of your control, then draw your icon and then call the parents paint.
try to change the following functio of your form to invalidate the buffer where the icon was before and thereby to remove the rest (untestet code):
private void panel1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
Point oldPos = myLine.StartPoint;
myLine.StartPoint = e.Location;
this.Invalidate(new Recangle(oldPos.X, oldPos.Y, myLine.Width, myLine.Height));
}
}
if this doesn't work, try:
private void panel1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
myLine.StartPoint = e.Location;
this.Refresh();
}
}
This method may not be recommended because of performace issues (the whole buffer is cleared), but of nothing else is working...
just adding another, completely different (hopefully again working :-) solution. As i wasn't aware of the requirement 'code has to be inside the class', this is the follow up.
Axiom: NEVER call Invalidate()
or Refresh()
in OnPaint or OnPaintBackground, because you will (always) end up in a infinite loop.
So we have to find another place for them. I tried to compile your classes within Visual Studio, but i couldn't find the class LineShape (Microsoft.VisualBasic.PowerPacks.Vs.dll didn't do the trick), so again untestet code.
what did i do?
- Removed the MouseMove handler from Form and put it inside the IconLineShape class. Shall be okay, because if customer wants drag and drop, just fine. Try one of the described solutions.
- Added a property to disable drag and drop in IconLineShape (if customer is not fine with that :-). without drag and drop, we would not have the problem in the first place.
-
public class Form1 : Form
{
IconLineShape myLine = new IconLineShape();
ShapeContainer shapeContainer1 = new ShapeContainer();
Panel panel1 = new Panel();
public Form1()
{
this.panel1.Dock = DockStyle.Fill;
// load your back image here
this.panel1.BackgroundImage =
global::WindowsApplication22.Properties.Resources._13820t;
this.panel1.Controls.Add(shapeContainer1);
this.myLine.StartPoint = new Point(20, 30);
this.myLine.EndPoint = new Point(80, 120);
this.myLine.Parent = this.shapeContainer1;
this.Controls.Add(panel1);
}
}
public class IconLineShape : LineShape
{
Icon myIcon = SystemIcons.Exclamation;
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
e.Graphics.DrawIcon(myIcon, StartPoint.X, StartPoint.Y);
base.OnPaint(e);
}
protected override void OnMouseMove(MouseEventArgs e)
{
if (draggable &&
e.Button == MouseButtons.Left &&
!this.StartPoint.Equals(e.Location))
{
Region r = this.Region.Clone();
this.StartPoint = e.Location;
// try solution 1
this.Invalidate(r);
// solution 2; walk up to the upmost parent and refresh
// as said before, this.Invalidate() is to be preferred
Control currentParent = this.Parent;
while (currentParent.Parent != null)
{
currentParent = currentParent.Parent;
}
currentParent.Refresh();
}
}
private bool draggable = true;
public bool Draggable
{
get { return this.draggable; }
set { this.draggable = value; }
}
}
Please give feedback.
did you do this on the custom control?
to remove the "stroboscope" effects
Public Sub New()
Me.SetStyle(ControlStyles.ResizeRedraw Or _
ControlStyles.DoubleBuffer Or _
ControlStyles.UserPaint Or _
ControlStyles.AllPaintingInWmPaint, _
True)
Me.UpdateStyles()
End Sub
Finally, ended up to add a PictureBox
instead of drawing the icon by myself.
using Microsoft.VisualBasic.PowerPacks;
using System.Windows.Forms;
using System.Drawing;
namespace LineShapeTest
{
///
/// Test Form
///
public class Form1 : Form
{
IconLineShape myLine = new IconLineShape();
ShapeContainer shapeContainer1 = new ShapeContainer();
Panel panel1 = new Panel();
public Form1()
{
this.panel1.Dock = DockStyle.Fill;
// load your back image here
//this.panel1.BackgroundImage =
//global::WindowsApplication22.Properties.Resources._13820t;
this.panel1.BackColor = Color.White;
this.panel1.Controls.Add(shapeContainer1);
this.myLine.StartPoint = new Point(20, 30);
this.myLine.EndPoint = new Point(80, 120);
this.myLine.Parent = this.shapeContainer1;
MouseEventHandler panelMouseMove =
new MouseEventHandler(this.panel1_MouseMove);
this.panel1.MouseMove += panelMouseMove;
this.shapeContainer1.MouseMove += panelMouseMove;
this.Controls.Add(panel1);
}
private void panel1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
myLine.StartPoint = e.Location;
}
}
}
///
/// Test LineShape
///
public class IconLineShape : LineShape
{
Icon myIcon = SystemIcons.Exclamation;
PictureBox pictureBox = new PictureBox();
public IconLineShape()
{
pictureBox.Image = Bitmap.FromHicon(myIcon.Handle);
pictureBox.Size = myIcon.Size;
pictureBox.Visible = true;
}
protected override void OnMove(System.EventArgs e)
{
base.OnMove(e);
pictureBox.Location = this.StartPoint;
}
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
base.OnPaint(e);
pictureBox.Invalidate();
}
public override bool HitTest(int x, int y)
{
return base.HitTest(x, y) |
pictureBox.RectangleToScreen(
pictureBox.DisplayRectangle).Contains(new Point(x, y));
}
protected override void OnParentChanged(System.EventArgs e)
{
// Parent is a ShapeContainer
// Parent.Parent is a Panel
pictureBox.Parent = Parent.Parent;
base.OnParentChanged(e);
}
}
}
Thanks everybody for participating!
Does this help? It seems you are too caught up with the LineShape. To me, deriving from RectangleShape made more sense. I wrapped everthing up in a helper that takes care of the details. This helper is a standard technique I use for relating controls together without creating a "composite control", which is usually easier.
using Microsoft.VisualBasic.PowerPacks;
using System.Windows.Forms;
using System.Drawing;
namespace LineShapeTest {
public partial class Form1 : Form {
/* Designer support through
* Create Panel
* Set panel's background image
* Add LineShape
* Add IconShape
* Create IconicLineShapeHelper
*/
public Form1() {
InitializeComponent();
IconicLineShapeHelper arbitrary1 = new IconicLineShapeHelper(lineShape1, iconShape1);
IconicLineShapeHelper arbitrary2 = new IconicLineShapeHelper(lineShape2, iconShape2);
}
private Panel panel1;
private ShapeContainer shapeContainer1;
private LineShape lineShape1;
private IconShape iconShape1;
private ShapeContainer shapeContainer2;
private LineShape lineShape2;
private IconShape iconShape2;
#region [ Form1.Designer.cs ]
private System.ComponentModel.IContainer components = null;
protected override void Dispose(bool disposing) {
if (disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
private void InitializeComponent() {
this.lineShape1 = new Microsoft.VisualBasic.PowerPacks.LineShape();
this.panel1 = new System.Windows.Forms.Panel();
this.shapeContainer1 = new Microsoft.VisualBasic.PowerPacks.ShapeContainer();
this.iconShape1 = new LineShapeTest.IconShape();
this.shapeContainer2 = new Microsoft.VisualBasic.PowerPacks.ShapeContainer();
this.lineShape2 = new Microsoft.VisualBasic.PowerPacks.LineShape();
this.iconShape2 = new LineShapeTest.IconShape();
this.panel1.SuspendLayout();
this.SuspendLayout();
//
// lineShape1
//
this.lineShape1.Name = "lineShape1";
this.lineShape1.X1 = 13;
this.lineShape1.X2 = 88;
this.lineShape1.Y1 = 11;
this.lineShape1.Y2 = 34;
//
// panel1
//
this.panel1.BackgroundImage = global::LineShapeTest.Properties.Resources._13820t;
this.panel1.Controls.Add(this.shapeContainer1);
this.panel1.Location = new System.Drawing.Point(27, 24);
this.panel1.Name = "panel1";
this.panel1.Size = new System.Drawing.Size(162, 122);
this.panel1.TabIndex = 1;
//
// shapeContainer1
//
this.shapeContainer1.Location = new System.Drawing.Point(0, 0);
this.shapeContainer1.Margin = new System.Windows.Forms.Padding(0);
this.shapeContainer1.Name = "shapeContainer1";
this.shapeContainer1.Shapes.AddRange(new Microsoft.VisualBasic.PowerPacks.Shape[] {
this.iconShape1,
this.lineShape1});
this.shapeContainer1.Size = new System.Drawing.Size(162, 122);
this.shapeContainer1.TabIndex = 0;
this.shapeContainer1.TabStop = false;
//
// iconShape1
//
this.iconShape1.BorderStyle = System.Drawing.Drawing2D.DashStyle.Custom;
this.iconShape1.Location = new System.Drawing.Point(88, 64);
this.iconShape1.Name = "iconShape1";
this.iconShape1.Size = new System.Drawing.Size(32, 32);
//
// shapeContainer2
//
this.shapeContainer2.Location = new System.Drawing.Point(0, 0);
this.shapeContainer2.Margin = new System.Windows.Forms.Padding(0);
this.shapeContainer2.Name = "shapeContainer2";
this.shapeContainer2.Shapes.AddRange(new Microsoft.VisualBasic.PowerPacks.Shape[] {
this.iconShape2,
this.lineShape2});
this.shapeContainer2.Size = new System.Drawing.Size(292, 266);
this.shapeContainer2.TabIndex = 2;
this.shapeContainer2.TabStop = false;
//
// lineShape2
//
this.lineShape2.Name = "lineShape2";
this.lineShape2.X1 = 48;
this.lineShape2.X2 = 123;
this.lineShape2.Y1 = 187;
this.lineShape2.Y2 = 210;
//
// iconShape2
//
this.iconShape2.Location = new System.Drawing.Point(136, 220);
this.iconShape2.Name = "iconShape2";
this.iconShape2.Size = new System.Drawing.Size(75, 23);
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(292, 266);
this.Controls.Add(this.panel1);
this.Controls.Add(this.shapeContainer2);
this.Name = "Form1";
this.Text = "Form1";
this.panel1.ResumeLayout(false);
this.ResumeLayout(false);
}
#endregion
}
#endregion
public class IconicLineShapeHelper {
ShapeContainer _container;
LineShape _line;
IconShape _icon;
public IconicLineShapeHelper(LineShape line, IconShape icon) {
_container = line.Parent;
_line = line;
_icon = icon;
_container.MouseMove += new MouseEventHandler(_container_MouseMove);
}
void _container_MouseMove(object sender, MouseEventArgs e) {
if (e.Button == MouseButtons.Left) {
_line.StartPoint = e.Location;
_icon.Location = e.Location;
}
}
}
public class IconShape : RectangleShape {
Icon _icon = SystemIcons.Exclamation;
public IconShape() {
this.Size = new System.Drawing.Size(32, 32);
this.BorderStyle = System.Drawing.Drawing2D.DashStyle.Custom;
}
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e) {
e.Graphics.DrawIcon(_icon, this.Location.X, this.Location.Y);
base.OnPaint(e);
}
}
}
精彩评论