I have a drag and drop interface in my Universal Windows App, where the user can add a textbox to the image, and drag and scale it around, as if it was a textbox in MSPaint, but it can be edited after it is placed, by double-tapping it.
I have implemented this as such:
textBox.LostFocus sets textBox.IsReadOnly = true;
textBox.DoubleTapped sets IsReadOnly back to false, so that it can drag around.
textBox.ManipulationDelta moves the textBox around.
Now I have the problem that the textBox has this rubberbanding effect going on, and that this blocks the manipulation event.
Is there a way to disable this? Or can I for example say when the mouse clicks (or finger or whatever), that it should start the manipulation event?
Code for a new TextBox:
private TextBox CreateManipulativeTextBox(string text)
{
var textBox = new TextBox
{
Text = text,
Background = new SolidColorBrush(Color.FromArgb(0, 0, 0, 0)),
BorderBrush = new SolidColorBrush(Color.FromArgb(128, 0, 0, 0)),
Width = 150,
Height = 50,
ManipulationMode = ManipulationModes.TranslateX | ManipulationModes.TranslateY | ManipulationModes.Scale,
RenderTransform = new CompositeTransform()
};
// Doubletap gives edit possibility, tap outside loses editability
textBox.LostFocus += TextBoxOnLostFocus;
textBox.DoubleTapped += TextBoxOnDoubleTapped;
// Add manipulation events
textBox.ManipulationStarted += OnManipulationStarted;
textBox.ManipulationDelta += OnManipulationDelta;
textBox.ManipulationCompleted += OnManipulationCompleted;
return textBox;
}
Code for focus and defocus:
private void TextBoxOnDoubleTapped(object sender, DoubleTappedRoutedEventArgs doubleTappedRoutedEventArgs)
{
(sender as TextBox).IsReadOnly = false;
}
private void TextBoxOnLostFocus(object sender, RoutedEventArgs routedEventArgs)
{
(sender as TextBox).IsReadOnly = true;
}
Code for manipulation:
private void OnManipulationStarted(object sender, ManipulationStartedRoutedEventArgs e)
{
if (sender is TextBox)
(sender as TextBox).IsReadOnly = true;
((FrameworkElement)sender).Opacity = 0.6;
}
private void OnManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e)
{
var frameworkElement = (FrameworkElement)sender;
var transform = (CompositeTransform)frameworkElement.RenderTransform;
// LEFT-RIGHT bounds
if (e.Delta.Translation.X < 0) // Going left
{
if (DrawingArea.ActualWidth / 2 + (transform.TranslateX + e.Delta.Translation.X) - frameworkElement.ActualWidth / 2 > 0)
{
// Staying inside, apply translation
transform.TranslateX += e.Delta.Translation.X;
}
else
{
// Trying to go outside, because scale sucks to work with, move image back inside
transform.TranslateX = frameworkElement.ActualWidth / 2 - DrawingArea.ActualWidth / 2;
}
}
else // Going right
{
if (DrawingArea.ActualWidth / 2 - (transform.TranslateX + e.Delta.Translation.X) +
frameworkElement.ActualWidth * (0.5 - transform.ScaleX) > 0)
{
// Staying inside, apply translation
transform.TranslateX += e.Delta.Translation.X;
}
else
{
// Trying to go outside, because scale sucks to work with, move image back inside
transform.TranslateX = frameworkElement.ActualWidth * (0.5 - transform.ScaleX) + DrawingArea.ActualWidth / 2;
}
}
// UP-DOWN bounds
if (e.Delta.Translation.Y < 0) // Going up
{
if (DrawingArea.ActualHeight / 2 + (transform.TranslateY + e.Delta.Translation.Y) - frameworkElement.ActualHeight / 2 >
0)
{
// Staying inside, apply translation
transform.TranslateY += e.Delta.Translation.Y;
}
else
{
// Trying to go outside, because scale sucks to work with, move image back inside
transform.TranslateY = frameworkElement.ActualHeight / 2 - DrawingArea.ActualHeight / 2;
}
}
else // Going down
{
if (DrawingArea.ActualHeight / 2 - (transform.TranslateY + e.Delta.Translation.Y) +
frameworkElement.ActualHeight * (0.5 - transform.ScaleY) > 0)
{
// Staying inside, apply translation
transform.TranslateY += e.Delta.Translation.Y;
}
else
{
// Trying to go outside, because scale sucks to work with, move image back inside
// transform.TranslateY = image.ActualHeight*(0.5 - transform.ScaleY) + DrawingArea.ActualHeight/2;
// Dragging down, remove image
DrawingArea.Children.Remove(frameworkElement);
}
}
// Only allow scaling when both dimensions are smaller than the drawingarea
if (frameworkElement.ActualHeight * (transform.ScaleY * e.Delta.Scale) < DrawingArea.ActualHeight &&
frameworkElement.ActualWidth * (transform.ScaleX * e.Delta.Scale) < DrawingArea.ActualWidth)
{
transform.ScaleX *= e.Delta.Scale;
transform.ScaleY *= e.Delta.Scale;
}
}
private void OnManipulationCompleted(object sender, ManipulationCompletedRoutedEventArgs e)
{
((FrameworkElement)sender).Opacity = 1;
}
EDIT: This only happens with touch, when the mouse drags the item around it works.
Related
I'm trying to design a button that has the DockChanged EH that knows the height & width of its paren't container (panel) and adjusts its metrics accordingly. E.g. button has text and icon, but if button width is in a panel that is too small, it will just show its icon and disable the text, etc.
Here's the method i'm trying to invoke the method with:
InnerPanel.DockChanged += HandleDockChanged;
Here's the method i'm trying to invoke the method on:
private void HandleDockChanged(EventArgs args)
{
//sorry for magic numbers
if (Parent.Height < Height || Parent.Width < Width)
{
TextLabel.Enabled = false;
InnerPanel.Width = ButtonWidth;
InnerPanel.Height = ButtonHeight;
Padding = new Padding(1);
}else
{
TextLabel.Enabled = true;
InnerPanel.Width = ButtonWidth * 2;
InnerPanel.Height = ButtonWidth * 2;
}
}
Okay, so I have a Grid with 4 child Images, each positioned in all four corners of the Grid like so.
The purpose of the bottom two Child Images, is to stretch the grid. So when the user drags these corners, the Grid will stretch.
My problem is that the Children will stretch too, which I don't want.
Is there any way to ensure that the size of the children does not stretch with the Grid?
Thanks.
P.S. I could detach the Child Images from the Grid, but it is much simpler and more elegant this way as the x/y positions remain at the edges of the Grid perfectly, I just want to ensure they do not Scale with it, if possible.
Create Grid and add Children
//ImageGrid
ImageControl.Height = 500;
ImageControl.Width = 500;
ImageControl.ManipulationMode = ManipulationModes.All;
canvas.Children.Add(ImageControl);
ImageControl.Children.Add(tickBtn);
ImageControl.Children.Add(delBtn);
ImageControl.Children.Add(skewBtn);
ImageControl.Children.Add(strBtn);
ImageManipulation.Image_Drag = new CompositeTransform();
ImageManipulation.Image_Drag.CenterX = imW / 2;
ImageManipulation.Image_Drag.CenterY = imH / 2;
ImageControl.RenderTransform = ImageManipulation.Image_Drag;
strBtn.ManipulationDelta += ImageManipulation.resBtn_Manip;
skewBtn.ManipulationDelta += MaskManipulation.skewBtn_Manip;
dragControl.ManipulationDelta += ImageManipulation.movBtn_Manip;
Manipulation Class
public static CompositeTransform Image_Drag;
public static void resBtn_Manip(object sender, ManipulationDeltaRoutedEventArgs e)
{
Mask_Drag.ScaleX += e.Delta.Translation.X;
Mask_Drag.ScaleY += e.Delta.Translation.X;
}
public static void movBtn_Manip(object sender, ManipulationDeltaRoutedEventArgs e)
{
Mask_Drag.TranslateX += e.Delta.Translation.X;
Mask_Drag.TranslateY += e.Delta.Translation.Y;
}
public static void skewBtn_Manip(object sender, ManipulationDeltaRoutedEventArgs e)
{
Mask_Drag.ScaleX -= e.Delta.Translation.X;
Mask_Drag.ScaleY += e.Delta.Translation.Y;
if (Mask_Drag.ScaleX < 0.5)
{
Mask_Drag.ScaleX = 0.5;
}
if (Mask_Drag.ScaleY < 0.5)
{
Mask_Drag.ScaleY = 0.5;
}
}
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());
}
I am creating an application, in which user will be able to Upload an image and then Zoom in and Out the image on a particular location (current mouse pointer).
Also user should be able to drag the image to see other parts of the image when the image is zoomed.
I have implemented some functionality to achieve it but i am scaling the complete image. I want to know that how i can scale a particular portion of image, or scale the complete image and then point to that location where my current mouse pointer is placed.
Code:
private void DisplayIsdDiagram(BO.IsdDiagram IsdDiagram)
{
DoubleBuffered = true;
zoomFac = 1;
translateX = 0;
translateY = 0;
transStartX = 0f;
transStartY = 0f;
picIsdDiagram.BorderStyle = BorderStyle.Fixed3D;
bmp = new Bitmap(Image.FromStream(new MemoryStream(IsdDiagram.Image.ToArray())));
if (bmp.Width > bmp.Height)
{
ratio = (float)picIsdDiagram.Width / (float)bmp.Width;
translateRatio = (float)bmp.Width / (float)picIsdDiagram.Width;
}
else
{
ratio = (float)picIsdDiagram.Height / (float)bmp.Height;
translateRatio = (float)bmp.Height / (float)picIsdDiagram.Height;
}
//picIsdDiagram.Image = bmp;
picIsdDiagram.Refresh();
picIsdDiagram.MouseWheel += new MouseEventHandler(picIsdDiagram_MouseWheel);
}
private void picIsdDiagram_MouseWheel(object sender, MouseEventArgs e)
{
IsZooming = true;
if (e.Delta < 0)
{
if (zoomFac > 1)
zoomFac = zoomFac - (float)0.1;
}
else
{
if (zoomFac <= 5)
zoomFac = zoomFac + (float)0.1;
}
picIsdDiagram.Refresh();
IsZooming = false;
}
private void picIsdDiagram_MouseDown(object sender, MouseEventArgs e)
{
IsZooming = false;
IsMouseDown = true;
transStartX = e.X;
transStartY = e.Y;
}
private void picIsdDiagram_MouseUp(object sender, MouseEventArgs e)
{
IsZooming = false;
IsMouseDown = false;
translateX = translateX + ((e.X - transStartX) * (translateRatio / zoomFac));
translateY = translateY + ((e.Y - transStartY) * (translateRatio / zoomFac));
picIsdDiagram.Refresh();
}
private void picIsdDiagram_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
g.ScaleTransform(ratio * zoomFac, ratio * zoomFac);
if (IsZooming == false && IsMouseDown == true)
g.TranslateTransform(translateX, translateY);
g.DrawImage(bmp, 0, 0);
}
I tried to get the current mouse position from MouseHover event and tried to Translate the picture when only Zoom is done, but that is not working.
Also the Picture Box has some other multiple Picture boxes inside it, to show some representation on the big image. While scaling the Big Image, small images (inside images) should not be scaled. Although there position needs to be recalculated to show them at their real places even after zooming on the Big image.
So in above i am facing two problems:
1) To Zoom an Image at any particular location (current mouse pointer)
by scrolling.
2) To regenerate the coordinates of the sub images while
zooming and translating.
Any help that can guide me in correct direction.
Also if by any other means, i could be able to achieve this functionality.
Application : Windows
Control : Picture Box (Please suggest if any other control can be used, if not possible with this)
Language : C#
Waiting for response!
PictureEdit control provided by DevExpress 13.2
I have an Application where I put Pictureboxes on a Panel. After I successfully implemented Drag&Drop for the Pictureboxes, I wanted to add a Grid option to conviniently move the Pictureboxes on the Panel. The code I used is
private void PB14_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
if (grid)
{
if (MousePosition.X % 10 == 0)
{
PBList[14].Location = new Point(PList[parent].PointToClient(new Point(MousePosition.X, MousePosition.Y)).X, PBList[14].Location.Y);
}
if (MousePosition.Y % 10 == 0)
{
PBList[14].Location = new Point(PBList[14].Location.X, PList[parent].PointToClient(new Point(MousePosition.X, MousePosition.Y)).Y);
}
}
else
{
...
}
}
}
PList is a List of Panels, PList[parent] is the parent in which the picturebox (out of a Pictureboxlist) PBList[14] is.
The Problem is that the Picturebox is not smoothely moving, sometimes it doesnt move at all. I found out that some values for the % operation are better some are worse, for example if I put
if (MousePosition.X % 30 == 0)
in the if statement, it is worse than 10.
I put the values of the if() in labels and i saw that it would sometimes skip the calculation, means the value jumped from 9 to 1, skipping the pixel where it should be 0 and the Picturebox didnt move.
Do you know any better ways of calculating the mouse coordinates for this purpose?
You'll want to determine the closest pixel of the grid pixels to your mouse position.
PBList[14].Location = new Point(PList[parent].PointToClient(new Point(MousePosition.X - (MousePosition.X % 10) + 5, MousePosition.Y - (MousePosition.Y % 10) + 5)).X);
Although my confusion is setting in here about the required parameters for PointToClient.
The jist of it is, recalculate the position for each mouse move regardless of whether it is directly on your 'grid by 10s', assign the location if it is different from the last (to save resources on re-painting) and find the closest 'grid by 10s' position by subracting the co-ords remainder of 10 from the co-ord
The problem is you only move anything when the mouse is exactly over one of the grid points.
One way to do this is allowing an extra pixel on each side:
if ((MousePosition.X+1) % 30 < 3)
{
int newX = MousePosition.X + 1 - ((MousePosition.X+1) % 30);
PBList[14].Location = new Point(PList[parent].PointToClient(new Point(newX, MousePosition.Y)).X, PBList[14].Location.Y);
}
//Same for Y
I was able to add snap-to-grid behaviour to my typical drag-and-drop implementation. The grid is based on the Control's Height and Width. You can change this by replacing control.Height and control.Width with constants, if you prefer. You may also want to prevent the Control from being dragged off screen.
private void OnMouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
control.Left = ((e.X + control.Left) / control.Width) * control.Width;
control.Top = ((e.Y + control.Top) / control.Height) * control.Height;
}
}
If you want the Control to drag smoothly and only snap-to-grid when you release the mouse button instead:
private void OnMouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
control.Left += e.X - offset.X;
control.Top += e.Y - offset.Y;
}
}
private void OnMouseUp(object sender, MouseEventArgs e)
{
control.Left = ((control.Left + control.Width / 2) / control.Width) * control.Width;
control.Top = ((control.Top + control.Height / 2) / control.Height) * control.Height;
}
private void OnMouseDown(object sender, MouseEventArgs e)
{
offset = e.Location;
}