Помогите перекрасить линию
Я делаю пользовательский элемент управления (наследуется от VisualBasic.PowerPacks.LineShape
), которая должна быть нарисована как стандартная, но рядом с ней должна быть иконка.
Итак, я просто переопределил OnPaint
как это:
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
e.Graphics.DrawIcon(myIcon, StartPoint.X, StartPoint.Y);
base.OnPaint(e);
}
Теперь все в порядке, но когда мой контроль перемещается, значок все еще остается нарисованным в древнем месте.
Есть ли способ нарисовать это правильно?
http://lh4.ggpht.com/_1TPOP7DzY1E/S5gXmp7xYiI/AAAAAAAADHI/pa1OhpKYSoM/Untitled-2.png Реальная ситуация в проекте
КОД: пример кода для тестов
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, для lineShape:
Parent = ShapeContainer
Parent.Parent = Panel
Обновление 1 СЛЕДЫ
В этом варианте OnPaint у нас есть следы:
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
Graphics g = Parent.Parent.CreateGraphics();
g.DrawIcon(myIcon, StartPoint.X, StartPoint.Y);
base.OnPaint(e);
}
http://lh4.ggpht.com/_1TPOP7DzY1E/S5j29lutQ0I/AAAAAAAADH4/4yEnZd_hPjA/s800/Capture3.png
Обновление 2 BLINKS
В этом варианте OnPaint у нас есть мигающее изображение:
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);
}
http://lh5.ggpht.com/_1TPOP7DzY1E/S5j4Bam7hiI/AAAAAAAADIA/1hQWKyV8Fr0/s800/Capture4.png
Обновление 3: внешняя недействительность
Этот вариант работает хорошо, но... из внешнего вида класса IconLineShape:
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();
base.OnParentChanged(e);
}
}
Даже это решает проблему тестового примера, мне нужно, чтобы этот элемент управления был выполнен внутри элемента управления, потому что я не могу заставить внешние "клиенты" этого элемента управления не забыть сохранить старую область и лишить родительских прав каждый раз при изменении местоположение...
6 ответов
Наконец, в конечном итоге, чтобы добавить PictureBox
вместо того, чтобы рисовать иконку самостоятельно.
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);
}
}
}
Спасибо всем за участие!
Вы пытались очистить буфер (нарисовать прямоугольник заливки с цветом фона)? Также убедитесь, что вы обнулили области отсечения до размера вашего элемента управления, затем нарисуйте свой значок и затем назовите родителей рисовать.
Попробуйте изменить следующую функцию вашей формы, чтобы сделать недействительным буфер, в котором ранее находился значок, и тем самым удалить все остальное (непроверенный код):
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));
}
}
если это не работает, попробуйте:
private void panel1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
myLine.StartPoint = e.Location;
this.Refresh();
}
}
Этот метод не рекомендуется из-за проблем с производительностью (очищается весь буфер), но больше ничего не работает...
Просто добавив другое, совершенно другое (надеюсь, снова работающее:-) решение. Поскольку я не знал о требовании "код должен быть внутри класса", это продолжение.
Аксиома: НИКОГДА не звоните Invalidate()
или же Refresh()
в OnPaint или OnPaintBackground, потому что вы (всегда) окажетесь в бесконечном цикле.
Поэтому мы должны найти другое место для них. Я пытался скомпилировать ваши классы в Visual Studio, но я не смог найти класс LineShape (Microsoft.VisualBasic.PowerPacks.Vs.dll не сработал), поэтому снова непроверенный код.
что я сделал?
- Удалил обработчик MouseMove из формы и поместил его в класс IconLineShape. Все будет хорошо, потому что, если клиент хочет перетащить, просто отлично. Попробуйте одно из описанных решений.
- Добавлено свойство для отключения перетаскивания в IconLineShape (если клиент не согласен с этим:-). без перетаскивания у нас не было бы проблемы в первую очередь.
-
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; }
}
}
Пожалуйста, дайте отзыв.
Ты сделал это на пользовательском элементе управления?
убрать эффекты "стробоскопа"
Public Sub New()
Me.SetStyle(ControlStyles.ResizeRedraw Or _
ControlStyles.DoubleBuffer Or _
ControlStyles.UserPaint Or _
ControlStyles.AllPaintingInWmPaint, _
True)
Me.UpdateStyles()
End Sub
Это помогает? Кажется, вы слишком увлечены LineShape. Для меня производное от RectangleShape имело больше смысла. Я завернул все в помощника, который заботится о деталях. Этот помощник является стандартной техникой, которую я использую для связывания элементов управления вместе без создания "составного элемента управления", что обычно проще.
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);
}
}
}