I am creating a label control that allows resizing from the top and bottom of the label. It also only slides vertically when moved. I created the boundaries for its movement and for the bottom part of the resizable label, but I'm having trouble figuring out what I'm doing wrong for the top part. Whenever the mouse is dragged outside of the bounds from the top, it triggers the preset length of 450 pixels. I am using .SetBounds along with Math.Min.
using System;
using System.Drawing;
using System.Windows.Forms;
namespace EventTest
{
public partial class Form1 : Form
{
Point label1_MousePoint_01 = new Point();
Point label1_Start_01 = new Point();
Point label1_End_01 = new Point();
int label1_MouseSwitch_01;
public Form1()
{
InitializeComponent();
label1.MouseDown += Label1_MouseDown;
label1.MouseUp += Label1_MouseUp;
label1.MouseMove += Label1_MouseMove;
}
// Mouse Move Event
private void Label1_MouseMove(object sender, MouseEventArgs e)
{
if ((e.X < label1.Width && e.X > 0 && e.Y > 0 && e.Y < 4) || (e.X < label1.Width && e.X > 0 && e.Y > label1.Height - 4 && e.Y < label1.Height))
{
Cursor.Current = Cursors.SizeNS;
}
// Mouse Event Top Move
if (label1_MouseSwitch_01 == 1)
{
label1.SetBounds(0, Math.Max(e.Y + label1.Location.Y - label1_MousePoint_01.Y, 40), 120, Math.Min(label1.Height - e.Y + label1_MousePoint_01.Y, 450));
}
// Mouse Event Bottom Move
else if (label1_MouseSwitch_01 == 2)
{
label1.Height = Math.Min(660 - label1.Location.Y, e.Location.Y);
}
// Mouse Event Grab and Move
else if (label1_MouseSwitch_01 == 3)
{
int ny = Math.Min(Math.Max((label1.Location.Y - label1_MousePoint_01.Y + e.Y), 40), 660 - label1.Height);
label1.Location = new Point(0, ny);
}
}
// Mouse Up Event
private void Label1_MouseUp(object sender, MouseEventArgs e)
{
label1_MouseSwitch_01 = 0;
if (label1.Height < 20)
{
label1.Height = 20;
}
}
// Mouse Down Event
private void Label1_MouseDown(object sender, MouseEventArgs e)
{
label1_Start_01.Y = label1.Height;
label1_End_01.Y = label1.Top;
// Set Mouse Switch
if (e.Button == MouseButtons.Left)
{
label1_MousePoint_01 = e.Location;
if (e.Y > 0 && e.Y < 4)
{
Cursor.Current = Cursors.SizeNS;
label1_MouseSwitch_01 = 1;
}
else if (e.Y > label1.Height - 4 && e.Y < label1.Height)
{
Cursor.Current = Cursors.SizeNS;
label1_MouseSwitch_01 = 2;
}
else if (e.Y > 4 && e.Y < label1.Height - 4)
{
label1_MouseSwitch_01 = 3;
}
}
}
// Snap To Every 20 Pixels
public int RoundEr(int i)
{
return ((int)Math.Round(i / 20.0)) * 20;
}
}
}
Related
I'm giving my panels inside another panel (this panel is in a usercontrol) a fixed location and a maximum size that changes with the size of the panel there in. Neither the resize or location works properly. The resize does happen but its to quickly. The location is fine if you only have 1 pinpanel for output and input. When you have more then 1 the locations are fixed but you need to resize the panel to resize to see the other panels. Could you point me in the right direction if you see the problem?
I have a panel drawPanel in this case that i use as a sort of background for the usercontrol. Inside this drawPanel i'm placing pinpanels. I want these pinpanels to resize with the usercontrol and give them a fixed location
private void OnClickPinPanel(object source, EventArgs e)
{
if (source is PinPanel p)
{
int index;
if ((index = Array.IndexOf(inputPins, p)) >= 0)
{
ClickedPinPanel?.Invoke(index, true);
}
else
{
ClickedPinPanel?.Invoke(Array.IndexOf(outputPins, p), false);
}
}
//else log here
}
private void CreatePinPanels(bool isInput)
{
int x = 0;
int y = -(int)(this.Width * 0.05)/2;
if (isInput)
{
for (int i = 0; i < inputPins.Length; i++)
{
y += (i + 1) * (this.Height / inputPins.Length + 1);
inputPins[i] = new PinPanel()
{
Location = new Point(x, y),
Size = new Size((int)(this.Width * 0.05), (int)(this.Width * 0.05)),
Anchor = AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Right,
};
inputPins[i].Click += OnClickPinPanel;
}
}
else
{
x += this.Width - (int)(this.Width * 0.1);
for (int i = 0; i < outputPins.Length; i++)
{
y += (i + 1) * (this.Height / inputPins.Length+1);
outputPins[i] = new PinPanel()
{
Size = new Size((int)(this.Width * 0.1), (int)(this.Width * 0.1)),
Location = new Point(x, y),
Anchor = AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Right
};
outputPins[i].Click += OnClickPinPanel;
}
}
}
The result i get now is that the pinpanels get a fixed location but when you have more then 1 pinpanel, the location is wrong its like if he thinks that the usercontrol is bigger then it is Reality. In order to see all the pins i have to resize and get this After resize
I want it to look like this
expectations
Try something like this out.
Here is my test rig:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
numericUpDown1.Value = someChip1.NumberInputPins;
numericUpDown2.Value = someChip1.NumberOutputPins;
}
private void numericUpDown1_ValueChanged(object sender, EventArgs e)
{
someChip1.NumberInputPins = (int)numericUpDown1.Value;
}
private void numericUpDown2_ValueChanged(object sender, EventArgs e)
{
someChip1.NumberOutputPins = (int)numericUpDown2.Value;
}
}
Sample chip with 5 inputs and 3 outputs:
Here is my PinPanel UserControl (just draws an ellipse/pin the size of the control):
public partial class PinPanel : UserControl
{
public PinPanel()
{
InitializeComponent();
}
private void PinPanel_Paint(object sender, PaintEventArgs e)
{
Rectangle rc = new Rectangle(new Point(0, 0), new Size(this.ClientRectangle.Width - 1, this.ClientRectangle.Height - 1));
e.Graphics.DrawEllipse(Pens.Black, rc);
}
private void PinPanel_SizeChanged(object sender, EventArgs e)
{
this.Refresh();
}
}
And finally, my SomeChip UserControl:
public partial class SomeChip : UserControl
{
public event PinPanelClick ClickedPinPanel;
public delegate void PinPanelClick(int index, bool input);
private PinPanel[] inputPins;
private PinPanel[] outputPins;
private int _NumberInputPins = 2;
public int NumberInputPins
{
get {
return _NumberInputPins;
}
set
{
if (value > 0 && value != _NumberInputPins)
{
_NumberInputPins = value;
CreatePinPanels();
this.Refresh();
}
}
}
private int _NumberOutputPins = 1;
public int NumberOutputPins
{
get
{
return _NumberOutputPins;
}
set
{
if (value > 0 && value != _NumberOutputPins)
{
_NumberOutputPins = value;
CreatePinPanels();
this.Refresh();
}
}
}
public SomeChip()
{
InitializeComponent();
}
private void SomeChip_Load(object sender, EventArgs e)
{
CreatePinPanels();
RepositionPins();
}
private void SomeChip_SizeChanged(object sender, EventArgs e)
{
this.Refresh();
}
private void SomeChip_Paint(object sender, PaintEventArgs e)
{
int PinHeight;
// draw the input pin runs
if (inputPins != null)
{
PinHeight = (int)((double)this.Height / (double)_NumberInputPins);
for (int i = 0; i < NumberInputPins; i++)
{
int Y = (i * PinHeight) + (PinHeight / 2);
e.Graphics.DrawLine(Pens.Black, 0, Y, this.Width / 4, Y);
}
}
// draw the output pin runs
if (outputPins != null)
{
PinHeight = (int)((double)this.Height / (double)_NumberOutputPins);
for (int i = 0; i < NumberOutputPins; i++)
{
int Y = (i * PinHeight) + (PinHeight / 2);
e.Graphics.DrawLine(Pens.Black, this.Width - this.Width / 4, Y, this.Width, Y);
}
}
//draw the chip itself (takes up 50% of the width of the UserControl)
Rectangle rc = new Rectangle(new Point(this.Width / 4, 0), new Size(this.Width / 2, this.Height - 1));
using (SolidBrush sb = new SolidBrush(this.BackColor))
{
e.Graphics.FillRectangle(sb, rc);
}
e.Graphics.DrawRectangle(Pens.Black, rc);
RepositionPins();
}
private void CreatePinPanels()
{
if (inputPins != null)
{
for (int i = 0; i < inputPins.Length; i++)
{
if (inputPins[i] != null && !inputPins[i].IsDisposed)
{
inputPins[i].Dispose();
}
}
}
inputPins = new PinPanel[_NumberInputPins];
for (int i = 0; i < inputPins.Length; i++)
{
inputPins[i] = new PinPanel();
inputPins[i].Click += OnClickPinPanel;
this.Controls.Add(inputPins[i]);
}
if (outputPins != null)
{
for (int i = 0; i < outputPins.Length; i++)
{
if (outputPins[i] != null && !outputPins[i].IsDisposed)
{
outputPins[i].Dispose();
}
}
}
outputPins = new PinPanel[_NumberOutputPins];
for (int i = 0; i < outputPins.Length; i++)
{
outputPins[i] = new PinPanel();
outputPins[i].Click += OnClickPinPanel;
this.Controls.Add(outputPins[i]);
}
}
private void OnClickPinPanel(object sender, EventArgs e)
{
PinPanel p = (PinPanel)sender;
if (inputPins.Contains(p))
{
ClickedPinPanel?.Invoke(Array.IndexOf(inputPins, p), true);
}
else if (outputPins.Contains(p))
{
ClickedPinPanel?.Invoke(Array.IndexOf(inputPins, p), false);
}
}
private void RepositionPins()
{
int PinRowHeight, PinHeight;
if (inputPins != null)
{
PinRowHeight = (int)((double)this.Height / (double)_NumberInputPins);
PinHeight = (int)Math.Min((double)(PinRowHeight / 2), (double)this.Height * 0.05);
for (int i = 0; i < inputPins.Length; i++)
{
if (inputPins[i] != null && !inputPins[i].IsDisposed)
{
inputPins[i].SetBounds(0, (int)((i * PinRowHeight) + (PinRowHeight /2 ) - (PinHeight / 2)), PinHeight, PinHeight);
}
}
}
if (outputPins != null)
{
PinRowHeight = (int)((double)this.Height / (double)_NumberOutputPins);
PinHeight = (int)Math.Min((double)(PinRowHeight / 2), (double)this.Height * 0.05);
for (int i = 0; i < outputPins.Length; i++)
{
if (outputPins[i] != null && !outputPins[i].IsDisposed)
{
outputPins[i].SetBounds(this.Width - PinHeight, (int)((i * PinRowHeight) + (PinRowHeight / 2) - (PinHeight / 2)), PinHeight, PinHeight);
}
}
}
}
}
I'm trying to move a dynamically drawn rectangle inside the canvas. I’m able to draw the rectangle dynamically with in the canvas and while trying to move the rectangle inside the canvas I’m facing problem
XAML :
<Grid x:Name="Gridimage1" Margin="0,0,411,100">
<Image Name="image1" HorizontalAlignment="Left" Stretch="Fill" VerticalAlignment="Top"></Image>
<Canvas x:Name="BackPanel" Margin="20,67,0,0" Height="317" Width="331">
<Rectangle x:Name="selectionRectangle" Stroke="LightBlue" Fill="#220000FF"/>
</Canvas>
</Grid>
C# :
After drawing the rectangle dynamically I'm adding the below mouse events.
selectionRectangle.MouseLeftButtonDown += new MouseButtonEventHandler(Rect1_MouseDown);
selectionRectangle.MouseMove += new MouseEventHandler(Rectangle_MouseMove_1);
selectionRectangle.MouseUp += new MouseButtonEventHandler(Rect1_MouseUp);
# region "rectangle move"
private bool drag = false;
private Point startPt;
private int wid;
private int hei;
private Point lastLoc;
private double CanvasLeft, CanvasTop;
private void Rect1_MouseDown(object sender, MouseButtonEventArgs e)
{
drag = true;
Cursor = Cursors.Hand;
startPt = e.GetPosition(BackPanel);
wid = (int)selectionRectangle.Width;
hei = (int)selectionRectangle.Height;
lastLoc = new Point(Canvas.GetLeft(selectionRectangle), Canvas.GetTop(selectionRectangle));
Mouse.Capture((IInputElement)sender);
}
private void Rectangle_MouseMove_1(object sender, MouseEventArgs e)
{
try
{
if (drag)
{
var newX = (startPt.X + (e.GetPosition(BackPanel).X - startPt.X));
var newY = (startPt.Y + (e.GetPosition(BackPanel).Y - startPt.Y));
Point offset = new Point((startPt.X - lastLoc.X), (startPt.Y - lastLoc.Y));
CanvasTop = newY - offset.Y;
CanvasLeft = newX - offset.X;
selectionRectangle.SetValue(Canvas.TopProperty, CanvasTop);
selectionRectangle.SetValue(Canvas.LeftProperty, CanvasLeft);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
private void Rect1_MouseUp(object sender, MouseButtonEventArgs e)
{
drag = false;
Cursor = Cursors.Arrow;
Mouse.Capture(null);
}
#endregion
Problem: I'm able to move the rectangle all over the window. I want only to move the rectangle inside the canvas margin.
I'm able to move the rectangle outside the canvas
You should be able to get the Bounds of your selectionRectangle and see if they exceed the Width and/or Height of your canvas before committing the drag operation.
selectionRectangle.MouseMove += new MouseEventHandler(Rectangle_MouseMove_1);
private bool drag = false;
private Point startPt;
private int wid;
private int hei;
private Point lastLoc;
private double CanvasLeft, CanvasTop;
private void Rectangle_MouseMove_1(object sender, MouseEventArgs e)
{
try
{
if (drag)
{
var newX = (startPt.X + (e.GetPosition(BackPanel).X - startPt.X));
var newY = (startPt.Y + (e.GetPosition(BackPanel).Y - startPt.Y));
Point offset = new Point((startPt.X - lastLoc.X), (startPt.Y - lastLoc.Y));
CanvasTop = newY - offset.Y;
CanvasLeft = newX - offset.X;
// check if the drag will pull the rectangle outside of it's host canvas before performing
// TODO: protect against lower limits too...
if ((CanvasTop + selectionRectangle.Height > BackPanel.Height) || (CanvasLeft + selectionRectangle.Width > BackPanel.Width) || CanvasTop < 0 || CanvasLeft < 0)
{
return;
}
selectionRectangle.SetValue(Canvas.TopProperty, CanvasTop);
selectionRectangle.SetValue(Canvas.LeftProperty, CanvasLeft);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
if ((CanvasTop + selectionRectangle.ActualHeight > BackPanel.ActualHeight))
{
CanvasTop = BackPanel.ActualHeight - selectionRectangle.ActualHeight;
}
if (CanvasLeft + selectionRectangle.ActualWidth > BackPanel.ActualWidth)
{
CanvasLeft = BackPanel.ActualWidth - selectionRectangle.ActualWidth;
}
if (CanvasTop < 0)
{
CanvasTop = 0;
}
if (CanvasLeft < 0)
{
CanvasLeft = 0;
}
I tried to Google how to make drag & drop for UIElements on a Canvas, but couldn't find anything that I'm looking for.
I got a C# WPF application with a Window. Inside the Window I got a Canvas where I can add Images to.
What I want is to be able to Drag & Drop the Images, while staying within the Canvas' borders.
I also want this to be in code, so not in the xaml.
I got this in the function where I add/update the Images to the Canvas. The TODO's should be replaced for the Drag & Drop events.
Image img = ImageList[i].Image;
img.Name = "Image" + i;
// TODO: Drag and Drop event for Image
// TODO: Check if Left and Top are within Canvas (minus width / height of Image)
Canvas.SetLeft(img, Left); // Default Left when adding the image = 0
Canvas.SetTop(img, Top); // Default Top when adding the image = 0
MyCanvas.Children.Add(img);
OnPropertyChanged("MyCanvas");
PS: Though this is for later, if someone has code to drag and drop multiple images at once as an additional bonus, I would appreciate it.
Thanks in advance for the help.
Fixed my problem below, by using the following code:
img.AllowDrop = true;
img.PreviewMouseLeftButtonDown += this.MouseLeftButtonDown;
img.PreviewMouseMove += this.MouseMove;
img.PreviewMouseLeftButtonUp += this.PreviewMouseLeftButtonUp;
private object movingObject;
private double firstXPos, firstYPos;
private void MouseLeftButtonDown(object sender, MouseButtonEventArgs e) {
// In this event, we get the current mouse position on the control to use it in the MouseMove event.
Image img = sender as Image;
Canvas canvas = img.Parent as Canvas;
firstXPos = e.GetPosition(img).X;
firstYPos = e.GetPosition(img).Y;
movingObject = sender;
// Put the image currently being dragged on top of the others
int top = Canvas.GetZIndex(img);
foreach (Image child in canvas.Children)
if (top < Canvas.GetZIndex(child))
top = Canvas.GetZIndex(child);
Canvas.SetZIndex(img, top + 1);
}
private void PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e) {
Image img = sender as Image;
Canvas canvas = img.Parent as Canvas;
movingObject = null;
// Put the image currently being dragged on top of the others
int top = Canvas.GetZIndex(img);
foreach (Image child in canvas.Children)
if (top > Canvas.GetZIndex(child))
top = Canvas.GetZIndex(child);
Canvas.SetZIndex(img, top + 1);
}
private void MouseMove(object sender, MouseEventArgs e) {
if (e.LeftButton == MouseButtonState.Pressed && sender == movingObject) {
Image img = sender as Image;
Canvas canvas = img.Parent as Canvas;
double newLeft = e.GetPosition(canvas).X - firstXPos - canvas.Margin.Left;
// newLeft inside canvas right-border?
if (newLeft > canvas.Margin.Left + canvas.ActualWidth - img.ActualWidth)
newLeft = canvas.Margin.Left + canvas.ActualWidth - img.ActualWidth;
// newLeft inside canvas left-border?
else if (newLeft < canvas.Margin.Left)
newLeft = canvas.Margin.Left;
img.SetValue(Canvas.LeftProperty, newLeft);
double newTop = e.GetPosition(canvas).Y - firstYPos - canvas.Margin.Top;
// newTop inside canvas bottom-border?
if (newTop > canvas.Margin.Top + canvas.ActualHeight - img.ActualHeight)
newTop = canvas.Margin.Top + canvas.ActualHeight - img.ActualHeight;
// newTop inside canvas top-border?
else if (newTop < canvas.Margin.Top)
newTop = canvas.Margin.Top;
img.SetValue(Canvas.TopProperty, newTop);
}
}
This code allows me to drag-and-drop the Images inside the Canvas, without leaving the Canvas itself.
Now I just need to be able to do two more things:
Fix a little bug where my Mouse slips of the Image when I drag them around to fast. This happens quite often, even when I'm not even moving the dragging image around THAT fast.. Fixed by using the solution mentioned in my other question.
Making it able to drag-and-drop multiple images at once, preferably by selecting multiple first, and then drag-and-drop the whole bunch of them while staying inside the Canvas.
Will make a new Question for this.
I was did a project that uses a chunk of your code and does drag and drop on canvas, look into it and see if there be any difference, dont really have the time to check
private void pinCanvas_PreviewMouseLeftButtonDown_1(object sender, MouseButtonEventArgs e)
{
// Point pt = e.GetPosition(pinCanvas);
// Curosor.Text = String.Format("You are at ({0}in, {1}in) in window coordinates", (pt.X / (96 / 72)) * 1/72, (pt.Y / (96 / 72)) * 1/72);
}
bool captured = false;
double x_shape, x_canvas, y_shape, y_canvas;
UIElement source = null;
string elementName;
double elementHeight, elementWidth;
private void pinCanvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
setCanvasSize();
source = (UIElement)sender;
elementName = ((Label)source).Name;
switch (elementName)
{
case "pinTextBox" :
elementHeight = pinActualHeight;
elementWidth = pinActualWidth;
break;
case "serialTextBox" :
elementHeight = serialActualHeight;
elementWidth = serialActualWidth;
break;
case "batchTextBox" :
elementHeight = batchActualHeight;
elementWidth = batchActualWidth;
break;
}
Mouse.Capture(source);
captured = true;
x_shape = Canvas.GetLeft(source);
x_canvas = e.GetPosition(Maincanvas).X;
y_shape = Canvas.GetTop(source);
y_canvas = e.GetPosition(Maincanvas).Y;
}
private void pinCanvas_MouseMove(object sender, MouseEventArgs e)
{
if (captured)
{
double x = e.GetPosition(Maincanvas).X;
double y = e.GetPosition(Maincanvas).Y;
var xCond = Math.Round(appActivities.DIP2Inch(x_shape), 4).ToString();
var yCond = Math.Round(appActivities.DIP2Inch(y_shape), 4).ToString();
var name = ((Label)source).Name;
x_shape += x - x_canvas;
// if ((x_shape < Maincanvas.ActualWidth - elementWidth) && x_shape > 0)
// {
Canvas.SetLeft(source, x_shape);
switch (name)
{
case "pinTextBox" :
pinOffsetLeft.Text = xCond;
break;
case "serialTextBox" :
serialOffsetLeft.Text = xCond;
break;
case "batchTextBox" :
batchOffsetLeft.Text = xCond;
break;
}
// }
x_canvas = x;
y_shape += y - y_canvas;
// if (y_shape < Maincanvas.ActualHeight - elementHeight && y_shape > 0)
// {
Canvas.SetTop(source, y_shape);
switch (name)
{
case "pinTextBox":
pinOffsetTop.Text = yCond;
break;
case "serialTextBox":
serialOffsetTop.Text = yCond;
break;
case "batchTextBox":
batchOffsetTop.Text = yCond;
break;
}
// }
y_canvas = y;
}
}
private void pinCanvas_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
Mouse.Capture(null);
captured = false;
// MessageBox.Show((Canvas.GetTop(source)).ToString());
/* if (Canvas.GetTop(source) < 0)
{
Canvas.SetTop(source, 0);
}
if (Canvas.GetLeft(source) < 0)
{
Canvas.SetLeft(source, 0);
}
if (Canvas.GetLeft(source) > Maincanvas.ActualWidth - elementWidth)
{
// MessageBox.Show("Left Too Much " + (Canvas.GetLeft(source) * 1/96).ToString());
Canvas.SetLeft(source, Maincanvas.ActualWidth - elementWidth);
}
if (Canvas.GetTop(source) > Maincanvas.ActualHeight - elementHeight)
{
Canvas.SetTop(source, Maincanvas.ActualHeight - elementHeight);
} */
oneElemntTorched = true;
//MessageBox.Show(this.pinTextBox.ActualHeight.ToString() + ", " + this.pinTextBox.ActualWidth.ToString());
}
How to me to change a code that when drawing on picturebox of coordinate undertook not from the first pressing of a mouse, and from the left bottom corner of picturebox?
private void Pic_MouseUp(object sender, MouseEventArgs e)
{
toDraw = false;
}
private void Pic_MouseMove(object sender, MouseEventArgs e)
{
if (toDraw)
{
topLeft = new Point
{
X = pointBegin.X < e.X ? pointBegin.X : e.X,
Y = pointBegin.Y < e.Y ? pointBegin.Y : e.Y
};
bottomRight = new Point
{
X = pointBegin.X > e.X ? pointBegin.X : e.X,
Y = pointBegin.Y > e.Y ? pointBegin.Y : e.Y
};
Pic.Invalidate();
}
}
private void Pic_Paint(object sender, PaintEventArgs e)
{
e.Graphics.DrawRectangle(new Pen(Color.DarkOrange, 2),
new System.Drawing.Rectangle(topLeft.X, topLeft.Y,
bottomRight.X - topLeft.X, bottomRight.Y - topLeft.Y));
dotLeftX = topLeft.X;
X1.Text = "X1: " + dotLeftX.ToString();
dotLeftY = topLeft.Y;
Y1.Text = "Y1: " + dotLeftY.ToString();
dotRightX = bottomRight.X;
X2.Text = "X2: " + dotRightX.ToString();
dotRightY = bottomRight.Y;
Y2.Text = "Y2: " + dotRightY.ToString();
}
http://i.imgbox.com/aazyql16.gif
I have a rectangle shape in a stackpanel and i want to drag and drop in a grid with the mouse using WPF!
I appreciate it if someone can help me?
Thanks to everyone.
A very simple implementation follows. It simply handles the Mouse button down/up/move events of the Rectangle in order to position it along with mouse movement. There is no error checking and nothing to prevent the user from dragging the rectangle off of the Canvas and leaving it there.
XAML:
<Window x:Class="WpfApplication6.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Canvas Name="canvas">
<Rectangle
Name="rect"
Width="50"
Height="50"
Canvas.Left="0"
Canvas.Top="0"
Fill="Red"
MouseLeftButtonDown="rect_MouseLeftButtonDown"
MouseLeftButtonUp="rect_MouseLeftButtonUp"
MouseMove="rect_MouseMove"
/>
</Canvas>
</Grid>
</Window>
Code Behind:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WpfApplication6
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private bool _isRectDragInProg;
public MainWindow()
{
InitializeComponent();
}
private void rect_MouseLeftButtonDown( object sender, MouseButtonEventArgs e )
{
_isRectDragInProg = true;
rect.CaptureMouse();
}
private void rect_MouseLeftButtonUp( object sender, MouseButtonEventArgs e )
{
_isRectDragInProg = false;
rect.ReleaseMouseCapture();
}
private void rect_MouseMove( object sender, MouseEventArgs e )
{
if( !_isRectDragInProg ) return;
// get the position of the mouse relative to the Canvas
var mousePos = e.GetPosition(canvas);
// center the rect on the mouse
double left = mousePos.X - (rect.ActualWidth / 2);
double top = mousePos.Y - (rect.ActualHeight / 2);
Canvas.SetLeft( rect, left );
Canvas.SetTop( rect, top );
}
}
}
For implementing drag and drop in an items control where the items are laid out by anything other than a canvas, then I would highly recommend checking out Bea Stollnitz solution. This is a completely reusable solution to drag and drop between any items control using attached properties.
Update: given that Bea's blog is gone, there are other posts that are based on her solution still available:
https://www.codeproject.com/Articles/43614/Drag-and-Drop-in-WPF
http://blogarchive.claritycon.com/blog/2009/03/generic-wpf-drag-and-drop-adorner
https://siderite.dev/blog/mvvm-drag-and-drop.html
Extending what #Ed S answered.The problem with using a Canvas is you can basically drag the controls beyond the boundaries. If you want to make sure it won't cross the boundaries, I created a simple application where if you have two shapes you can either move both of them together or drag individually. Ofcourse I have a checkbox with name ChkDual mode to decide if shapes have to move together or not. You can edit that functionality as you need.
<Grid>
<!--Set some height and width as you like-->
<Canvas x:Name="CnvBarCodeImage" AllowDrop="True">
</Canvas>
<Grid>
private void CreateUIShapes(int numberOfWindows)
{
//If you want multiple shapes put it inside a loop
Thumb th = null;
th = new Thumb();
th.Width = 200;
th.Height = 100;
th.Foreground = new SolidColorBrush(Windows.UI.Colors.Transparent);
th.BorderBrush = Colors.Green;
th.BorderThickness = new Thickness(3);
th.DragDelta += (sender, e) => Th_DragDelta(sender, e);
Canvas.SetLeft(th, 100);
Canvas.SetTop(th, 80);
CnvBarCodeImage.Children.Add(th);
}
private event Action<Thumb, double, double> ChangeDimensions;
private void Th_DragDelta(object sender, DragDeltaEventArgs e, List<Dimensions> limitedWindowMovements)
{
var selectedDraggableThumb = sender as Thumb;
double left = (Canvas.GetLeft(selectedDraggableThumb) >= 0) ? Canvas.GetLeft(selectedDraggableThumb) : 0;
double top = (Canvas.GetTop(selectedDraggableThumb) >= 0) ? Canvas.GetTop(selectedDraggableThumb) : 0;
if (ChkDualMode.IsChecked == true)
{
var otherDraggableThumbs = CnvBarCodeImage.Children.OfType<Thumb>().Where(x => x != selectedDraggableThumb).ToList();
var maxTopSelected = 0
var maxLeftSelected = 0;
//There may be n number of windows.
foreach (var item in otherDraggableThumbs)
{
//TOP
//Canvas.GetTop(SelectedDraggableThumb) <= 0 || Canvas.GetTop(item) <= 0 original.
if (Canvas.GetTop(selectedDraggableThumb) <= maxTopSelected || Canvas.GetTop(item) <= maxTopSelected)
{
if (e.VerticalChange <= 0)
{
ChangeDimensions -= BarCodeServiceView_ChangeDimensions;
return;
}
}
//LEFT
//Canvas.GetLeft(SelectedDraggableThumb) <= 0 || Canvas.GetLeft(item) <= 0 original.
else if (Canvas.GetLeft(selectedDraggableThumb) <= maxLeftSelected || Canvas.GetLeft(item) <= maxLeftSelected)
{
if (e.HorizontalChange <= 0)
{
ChangeDimensions -= BarCodeServiceView_ChangeDimensions;
return;
}
}
//RIGHT
else if (Canvas.GetLeft(selectedDraggableThumb) >=
CnvBarCodeImage.ActualWidth - selectedDraggableThumb.ActualWidth ||
Canvas.GetLeft(item) >= CnvBarCodeImage.ActualWidth - item.ActualWidth)
//|| Canvas.GetLeft(SelectedDraggableThumb) >= maxRightSelected
//|| Canvas.GetLeft(item) >= maxRightSelected)
{
if (e.HorizontalChange > 0)
{
ChangeDimensions -= BarCodeServiceView_ChangeDimensions;
return;
}
}
//BOTTOM
else if (Canvas.GetTop(selectedDraggableThumb) + selectedDraggableThumb.ActualHeight >= CnvBarCodeImage.ActualHeight ||
Canvas.GetTop(item) + item.ActualHeight >= CnvBarCodeImage.ActualHeight)
{
if (e.VerticalChange >= 0)
{
ChangeDimensions -= BarCodeServiceView_ChangeDimensions;
return;
}
}
if (ChangeDimensions == null)
{
ChangeDimensions += BarCodeServiceView_ChangeDimensions;
}
}
foreach (var item in otherDraggableThumbs)
{
ChangeDimensions?.Invoke(item, e.HorizontalChange, e.VerticalChange);
}
left = (Canvas.GetLeft(selectedDraggableThumb) >= CnvBarCodeImage.ActualWidth) ? CnvBarCodeImage.ActualWidth : left;
top = (Canvas.GetTop(selectedDraggableThumb) >= CnvBarCodeImage.ActualHeight) ? CnvBarCodeImage.ActualHeight : top;
Canvas.SetLeft(selectedDraggableThumb, left + e.HorizontalChange);
Canvas.SetTop(selectedDraggableThumb, top + e.VerticalChange);
}
else
{
//In single mode limit the movement as well.
int.TryParse(selectedDraggableThumb.Name, out var indexFromName);
var limitedDimensionForWindow = limitedWindowMovements[indexFromName];
var maxTop = limitedDimensionForWindow.MaxTopPossible == null ? 0 : limitedDimensionForWindow.MaxTopPossible;
var maxLeft = limitedDimensionForWindow.MaxLeftPossible == null ? 0 : limitedDimensionForWindow.MaxLeftPossible;
var maxRight = limitedDimensionForWindow.MaxRightPossible == null ? CnvBarCodeImage.ActualWidth - selectedDraggableThumb.ActualWidth : limitedDimensionForWindow.MaxRightPossible;
var maxBottom = limitedDimensionForWindow.MaxBottomPossible == null ? CnvBarCodeImage.ActualHeight - selectedDraggableThumb.ActualHeight : limitedDimensionForWindow.MaxBottomPossible;
if (Canvas.GetTop(selectedDraggableThumb) <= maxTop)
{
if (e.VerticalChange <= 0)
{
return;
}
}
else if (Canvas.GetLeft(selectedDraggableThumb) <= maxLeft)
{
if (e.HorizontalChange <= 0)
{
return;
}
}
else if (Canvas.GetLeft(selectedDraggableThumb) >= maxRight)
{
if (e.HorizontalChange >= 0)
{
return;
}
}
else if (Canvas.GetTop(selectedDraggableThumb) >= maxBottom)
{
if (e.VerticalChange >= 0)
{
return;
}
}
left = (Canvas.GetLeft(selectedDraggableThumb) >= CnvBarCodeImage.ActualWidth) ? CnvBarCodeImage.ActualWidth : left;
top = (Canvas.GetTop(selectedDraggableThumb) >= CnvBarCodeImage.ActualHeight) ? CnvBarCodeImage.ActualHeight : top;
Canvas.SetLeft(selectedDraggableThumb, left + e.HorizontalChange);
Canvas.SetTop(selectedDraggableThumb, top + e.VerticalChange);
}
}
private void BarCodeServiceView_ChangeDimensions(Thumb sender, double arg1, double arg2)
{
if (sender != null)
{
double left = (Canvas.GetLeft(sender) > 0) ? Canvas.GetLeft(sender) : 0;
double top = (Canvas.GetTop(sender) > 0) ? Canvas.GetTop(sender) : 0;
Canvas.SetLeft(sender, left + arg1);
Canvas.SetTop(sender, top + arg2);
}
}