英文:
Drag and Drop different graphic shapes within a Form individually
问题
以下是您提供的代码的中文翻译部分:
我试图能够选择、垂直移动并放置两个不同的矩形(可能获取它们的位置,但这是另一天的问题)。
以下是我为一个形状编写的代码,但我找不到一种方法,使得可以通过点击一个而不影响另一个来执行操作。
使用 System.Collections.Generic;
使用 System.Drawing;
使用 System.Linq;
public 部分类 Form1 : Form
{
矩形 topMarker = new 矩形(50, 125, 50, 2);
矩形 bottomMarker = new 矩形(50, 200, 50, 2);
布尔值 isMouseDown = 假;
public Form1()
{
初始化组件();
}
私有 空 pictureBox1_绘制(对象 sender, PaintEventArgs e)
{
e.Graphics.FillRectangle(new SolidBrush(Color.Green), topMarker);
e.Graphics.FillRectangle(new SolidBrush(Color.Red), bottomMarker);
}
私有 空 pictureBox1_鼠标按下(对象 sender, MouseEventArgs e)
{
isMouseDown = 真;
}
私有 空 pictureBox1_鼠标移动(对象 sender, MouseEventArgs e)
{
如果 (isMouseDown == 真)
{
topMarker.Location = e.Location;
topMarker.X = 50;
如果 (topMarker.Y < 0)
{
topMarker.Y = 0;
}
如果 (topMarker.Y > pictureBox1.Height)
{
topMarker.Y = pictureBox1.Height - topMarker.Height;
}
刷新();
}
}
私有 空 pictureBox1_鼠标抬起(对象 sender, MouseEventArgs e)
{
isMouseDown = 假;
}
}
希望这可以帮助您理解代码的功能。如果您有任何进一步的问题,请随时提出。
英文:
I am trying to be able to pick, move vertically and drop 2 different rectangles (potentially getting their positions, but that's a problem for another day).
Here's the code that I managed to put together for one shape, but couldn't I find a way to make it possible to perform the action by clicking on one without affecting the other.
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
public partial class Form1 : Form
{
Rectangle topMarker = new Rectangle(50, 125, 50, 2);
Rectangle bottomMarker = new Rectangle(50, 200, 50, 2);
bool isMouseDown = false;
public Form1()
{
InitializeComponent();
}
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.FillRectangle(new SolidBrush(Color.Green), topMarker);
e.Graphics.FillRectangle(new SolidBrush(Color.Red), bottomMarker);
}
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
isMouseDown = true;
}
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
if (isMouseDown == true)
{
topMarker.Location = e.Location;
topMarker.X = 50;
if (topMarker.Y < 0)
{
topMarker.Y = 0;
}
if (topMarker.Y > pictureBox1.Height)
{
topMarker.Y = pictureBox1.Height - topMarker.Height;
}
Refresh();
}
}
private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
{
isMouseDown = false;
}
}
答案1
得分: 1
以下是您提供的内容的翻译:
我建议创建一个描述形状的类对象。
在需要与其对象互动时,处理这些对象的集合要容易得多。
在我的观点中,类对象应该实现一个接口,允许调用方法和使用属性,这些方法和属性对您绘制的所有形状都是通用的(假设您可以添加不同的形状,而不仅仅是矩形)。
在这个示例中,Marker类对象实现了一个IShape接口,该接口定义了所有对象必须公开的方法和属性。
它还实现了IDisposable,因为它包含未托管资源,以这种情况下的GraphicsPath的形式。
请在窗体关闭时调用Dispose()
来释放集合中所有元素。
- Marker类的
Move()
方法允许指定偏移量(与前一个位置的差异)来移动形状。 - 它的
Draw()
方法接受一个Graphics
对象作为参数,您只需要传递由画布的Paint
事件提供的e.Graphics
对象即可。Marker在此设备上下文中绘制自己。 IsMouseOver()
在鼠标位置落在形状边界内时返回true或false。它使用GraphicsPath的IsVisible
属性。
为了避免在拖动形状时丢失形状(因为鼠标指针可能移动到其边界之外),当Marker对象接收到点击事件时,我们预先定义了一个“选定的形状”,并存储了指针的当前位置。然后,当鼠标移动时,它会提前更新。当前选定的形状也会更新(请参阅mouseTrackLocation
和selectedMarker
字段)。
这差不多就是全部内容了,除非您可以向识别鼠标指针下的形状的代码添加一些“容忍度”(可以通过将GraphicsPath膨胀为一些不可见内容来实现)。
注意:此代码假定您的目标是.NET 6+和启用了可空性。如果不是这种情况,只需将所有的object?
更改为object
,并在using
块中更改using
声明。
以下是Marker类和IShape接口的翻译:
public interface IShape {
RectangleF Shape { get; }
GraphicsPath Path { get; }
void Move(PointF position);
void Draw(Graphics graphics);
bool IsMouseOver(PointF mousePosition);
}
public class Marker : IShape, IDisposable {
private bool disposed = false;
private GraphicsPath path = new();
public Marker(RectangleF shape, Color borderColor, Color fillColor) {
BorderColor = borderColor;
FillColor = fillColor;
path.AddRectangle(shape);
Path = path;
}
public RectangleF Shape => path.GetBounds();
public GraphicsPath Path { get; }
public Color BorderColor { get; set; }
public Color FillColor { get; set; }
public bool IsMouseOver(PointF mousePosition) => path.IsVisible(mousePosition);
public void Move(PointF position) {
// 按偏移量移动,而不是绝对位置
using var mx = new Matrix(1, 0, 0, 1, position.X, position.Y);
path.Transform(mx);
}
public void Draw(Graphics canvas) {
using var brush = new SolidBrush(FillColor);
using var pen = new Pen(BorderColor);
canvas.FillPath(brush, path);
canvas.DrawPath(pen, path);
}
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing) {
if (!disposed) {
path?.Dispose();
disposed = true;
}
}
}
希望这有助于您理解原文中的内容!如果您有其他问题,请随时提出。
英文:
I suggest to create a class object that describes your shape.
A collection of these object is much easier to handle when you need to interact with one of its objects.
The class object, in my view, should implement an Interface that allows to call methods and use properties that are common to all the shapes you draw (assuming you could add different shapes, not just rectangles).
In the example here, the Marker class objects implements an IShape Interface, which defines which methods and properties all objects must expose.
It also implements IDisposable, since it holds unmanaged resources, in the form of a GraphicsPath, in this case. Remember to call Dispose()
on all elements of the collection when the Form closes.
- The Marker's class
Move()
method allows to specify an offset (difference from the previous position) to move a shape. - Its
Draw()
method accepts aGraphics
object as argument, you just need to pass thee.Graphics
object provided by thePaintEventArgs
of thePaint
event of the canvas where the shapes are drawn. The Marker draws itself in this device context. - The
IsMouseOver()
returns true or false when the Mouse position falls inside the bounds of the shape. It uses the GraphicsPath's IsVisible property
To avoid losing a shape while it's being dragged around (because the Mouse Pointer may move outside it's bounds), we pre-define a selected shape when one of the Marker objects receives a click event and we store the current position of the Pointer. It's then updated in advance while the Mouse is moved. The currently selected shape is also updated (see the mouseTrackLocation
and selectedMarker
Fields)
Thats' pretty much all about it, except you could add some tolerance to the code that identifies the shape under the Mouse Pointer (could be done inflating the GraphicsPath with some invisible content)
NOTE: This code assumes you're targeting .NET 6+ and nullable enabled. If this is not the case, simply change all object?
to object
and using
declarations in using
blocks
<br/>
public partial class SomeForm : Form {
private List<Marker> markers = new();
private bool dragShapeStarted = false;
private Point mouseTrackLocation = Point.Empty;
private Marker? selectedMarker = null;
public SomeForm() {
InitializeComponent();
markers.AddRange(new[]{
new Marker(new RectangleF(50, 125, 100, 2), Color.Transparent, Color.Green),
new Marker(new RectangleF(50, 200, 100, 2), Color.Transparent, Color.Red),
new Marker(new RectangleF(50, 250, 100, 2), Color.Transparent, Color.Orange),
});
}
private void somePictureBox_MouseDown(object? sender, MouseEventArgs e) {
if (e.Button == MouseButtons.Left) {
selectedMarker = markers.FirstOrDefault(m => m.IsMouseOver(e.Location));
// Mouse Down on a shape & selectedMarker not null => pre-emptive positive result
if (selectedMarker != null) dragShapeStarted = true;
mouseTrackLocation = e.Location;
}
}
private void somePictureBox_MouseMove(object? sender, MouseEventArgs e) {
if (!(sender is PictureBox pBox)) return;
pBox.Cursor = selectedMarker is null ? Cursors.Default : Cursors.HSplit;
// We need to move the selected shape as quick as possible: see MouseDown
if (selectedMarker != null && dragShapeStarted) {
selectedMarker.Move(new PointF(0, e.Location.Y - mouseTrackLocation.Y));
mouseTrackLocation = e.Location;
pBox.Invalidate();
}
// Enables curson change while the Mouse Pointer is moved
selectedMarker = markers.FirstOrDefault(m => m.IsMouseOver(e.Location));
}
private void somePictureBox_MouseUp(object? sender, MouseEventArgs e) {
dragShapeStarted = false;
selectedMarker = null;
}
private void somePictureBox_Paint(object? sender, PaintEventArgs e) {
markers.ForEach(m => m.Draw(e.Graphics));
}
}
The Marker class and IShape Interface:
public interface IShape {
RectangleF Shape { get; }
GraphicsPath Path { get; }
void Move(PointF position);
void Draw(Graphics graphics);
bool IsMouseOver(PointF mousePosition);
}
public class Marker : IShape, IDisposable {
private bool disposed = false;
private GraphicsPath path = new();
public Marker(RectangleF shape, Color borderColor, Color fillColor) {
BorderColor = borderColor;
FillColor = fillColor;
path.AddRectangle(shape);
Path = path;
}
public RectangleF Shape => path.GetBounds();
public GraphicsPath Path { get; }
public Color BorderColor { get; set; }
public Color FillColor { get; set; }
public bool IsMouseOver(PointF mousePosition) => path.IsVisible(mousePosition);
public void Move(PointF position) {
// Translates by an offset, not absolute position
using var mx = new Matrix(1, 0, 0, 1, position.X, position.Y);
path.Transform(mx);
}
public void Draw(Graphics canvas) {
using var brush = new SolidBrush(FillColor);
using var pen = new Pen(BorderColor);
canvas.FillPath(brush, path);
canvas.DrawPath(pen, path);
}
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing) {
if (!disposed) {
path?.Dispose();
disposed = true;
}
}
}
This is how it works:
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论