objeto - Cómo arrastrar y mover formas en C#
gdi+ c# (1)
En una aplicación C # WindoeFormsAplicación, ¿es posible seleccionar, por lo tanto, mover o eliminar una forma trazada con el mouse? Al igual que el programa de pintura de Windows.
El trazado de formas funciona totalmente bien, todos los puntos se almacenan en alguna matriz. Como este ejemplo de dibujo lineal
Point Latest { get; set; }
List<Point> _points = new List<Point>();
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
// Save the mouse coordinates
Latest = new Point(e.X, e.Y);
// Force to invalidate the form client area and immediately redraw itself.
Refresh();
}
protected override void OnPaint(PaintEventArgs e)
{
var g = e.Graphics;
base.OnPaint(e);
if (_points.Count > 0)
{
var pen = new Pen(Color.Navy);
var pt = _points[0];
for(var i=1; _points.Count > i; i++)
{
var next = _points[i];
g.DrawLine(pen, pt, next);
pt = next;
}
g.DrawLine(pen, pt, Latest);
}
}
private void Form1_MouseClick(object sender, MouseEventArgs e)
{
Latest = new Point(e.X, e.Y);
_points.Add(Latest);
Refresh();
}
Puedo dejar que calcule la distancia más corta entre la posición del mouse y cada línea mediante álgebra lineal básica, y establecer una distancia umbral, si es más corta que el umbral, seleccione esta línea y puede arrastrarla o editarla con el mouse. Pero, preguntándome, ¿hay alguna forma más manejable para tal tarea? Principalmente la parte de selección. Cualquier sugerencia será apreciada, gracias!
Para golpear formas de prueba no necesita álgebra lineal.
Puede crear
GraphicsPath
para sus formas y luego usar el método
GraphicsPath.IsOutlineVisible
o
GraphicsPath.IsOutlineVisible
para realizar pruebas de impacto.
-
Para verificar si un punto está en el área de su camino, por ejemplo, una forma rellena, use
IsVisible
. -
Para probar las líneas, curvas o formas vacías, puede usar
IsOutlineVisible
.
Ejemplo
Como ejemplo, puede crear una interfaz básica de
IShape
que contenga métodos para probar, dibujar y mover.
Luego, en las clases, implemente esos métodos.
También puede crear un control
DrawingSurface
que puede manejar pruebas de impacto, dibujar y mover objetos
IShape
.
En el siguiente ejemplo, creamos la interfaz
IShape
, clases de
Line
y
Circle
.
También creamos un control
DrawingSurface
.
Para probar el ejemplo, es suficiente poner un control
DrawingSurface
en un
Form
y manejar
Load
evento de formulario y agregar algunas formas, luego ejecutar la aplicación e intentar mover formas.
IShape
Esta interfaz contiene algunos métodos útiles que, si alguna clase los implementa, se pueden usar para dibujar, probar y mover.
Al final de este ejemplo, puede ver un control
IShape
que puede funcionar con implementaciones de
IShape
simplemente:
public interface IShape
{
GraphicsPath GetPath();
bool HitTest(Point p);
void Draw(Graphics g);
void Move(Point d);
}
Línea
Aquí hay una clase de línea que implementa la interfaz
IShape
.
Cuando se realiza una prueba de impacto si hace clic en la línea,
HitTest
devuelve verdadero.
Además, para permitirle elegir la línea de manera más simple, agregué 2 puntos para las pruebas de impacto:
public class Line : IShape
{
public Line() { LineWidth = 2; LineColor = Color.Black; }
public int LineWidth { get; set; }
public Color LineColor { get; set; }
public Point Point1 { get; set; }
public Point Point2 { get; set; }
public GraphicsPath GetPath()
{
var path = new GraphicsPath();
path.AddLine(Point1, Point2);
return path;
}
public bool HitTest(Point p)
{
var result = false;
using (var path = GetPath())
using (var pen = new Pen(LineColor, LineWidth + 2))
result = path.IsOutlineVisible(p, pen);
return result;
}
public void Draw(Graphics g)
{
using (var path = GetPath())
using (var pen = new Pen(LineColor, LineWidth))
g.DrawPath(pen, path);
}
public void Move(Point d)
{
Point1 = new Point(Point1.X + d.X, Point1.Y + d.Y);
Point2 = new Point(Point2.X + d.X, Point2.Y + d.Y);
}
}
Circulo
Aquí hay una clase de círculo que implementa la interfaz
IShape
.
Al hacer una prueba de golpe si hace clic en círculo, el
HitTest
devuelve verdadero:
public class Circle : IShape
{
public Circle() { FillColor = Color.Black; }
public Color FillColor { get; set; }
public Point Center { get; set; }
public int Radious { get; set; }
public GraphicsPath GetPath()
{
var path = new GraphicsPath();
var p = Center;
p.Offset(-Radious, -Radious);
path.AddEllipse(p.X, p.Y, 2 * Radious, 2 * Radious);
return path;
}
public bool HitTest(Point p)
{
var result = false;
using (var path = GetPath())
result = path.IsVisible(p);
return result;
}
public void Draw(Graphics g)
{
using (var path = GetPath())
using (var brush = new SolidBrush(FillColor))
g.FillPath(brush, path);
}
public void Move(Point d)
{
Center = new Point(Center.X + d.X, Center.Y + d.Y);
}
}
DibujoSuperficie
El control, dibuja una lista de formas.
También realiza pruebas de impacto en
MouseDown
y mueve la forma si la arrastra.
Debería agregar algunas formas como la colección
Line
o
Circle
to
Shapes
del control.
public class DrawingSurface : Control
{
public List<IShape> Shapes { get; private set; }
IShape selectedShape;
bool moving;
Point previousPoint = Point.Empty;
public DrawingSurface() { DoubleBuffered = true; Shapes = new List<IShape>(); }
protected override void OnMouseDown(MouseEventArgs e)
{
for (var i = Shapes.Count - 1; i >= 0; i--)
if (Shapes[i].HitTest(e.Location)) { selectedShape = Shapes[i]; break; }
if (selectedShape != null) { moving = true; previousPoint = e.Location; }
base.OnMouseDown(e);
}
protected override void OnMouseMove(MouseEventArgs e)
{
if (moving) {
var d = new Point(e.X - previousPoint.X, e.Y - previousPoint.Y);
selectedShape.Move(d);
previousPoint = e.Location;
this.Invalidate();
}
base.OnMouseMove(e);
}
protected override void OnMouseUp(MouseEventArgs e)
{
if (moving) { selectedShape = null; moving = false; }
base.OnMouseUp(e);
}
protected override void OnPaint(PaintEventArgs e)
{
e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
foreach (var shape in Shapes)
shape.Draw(e.Graphics);
}
}