How can I move a Picture with just "drag" the picture? - c#

So I want to make a funtunality like in Windows when you display a Picture that when you zoom in you can just move the Picture with the mouse. I already got the zoom part in my project:
<ScrollViewer Name="scroll" Margin="40"
HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto">
<Canvas
MouseWheel="Container_MouseWheel" x:Name="canvas">
<Viewbox>
<Image x:Name="img" Source="{Binding imageSource}" Grid.ColumnSpan="2" />
</Viewbox>
</Canvas>
</ScrollViewer>
and the Code in the MouseWheel Event:
var element = sender as UIElement;
var position = e.GetPosition(element);
var transform = element.RenderTransform as MatrixTransform;
var matrix = transform.Matrix;
var scale = e.Delta >= 0 ? 1.1 : (1.0 / 1.1); // choose appropriate scaling factor
matrix.ScaleAtPrepend(scale, scale, position.X, position.Y);
element.RenderTransform = new MatrixTransform(matrix);
but now Ive got the Problem that when I zoom I cant just click and hold the Image with the mouse and move to an another part of the picture, if you dont really know what I mean just open an Image on windows zoom in and then hold the mouse and move in the picture.
What I have tried was to add these 3 Events to my Canvas and this is the code behind for the 3 Events:
private Image draggedImage;
private Point mousePosition;
private void Canvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
var image = e.Source as Image;
if (image != null && canvas.CaptureMouse())
{
mousePosition = e.GetPosition(canvas);
draggedImage = image;
Panel.SetZIndex(draggedImage, 1); // in case of multiple images
}
}
private void Canvas_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (draggedImage != null)
{
canvas.ReleaseMouseCapture();
Panel.SetZIndex(draggedImage, 0);
draggedImage = null;
}
}
private void Canvas_MouseMove(object sender, MouseEventArgs e)
{
if (draggedImage != null)
{
var position = e.GetPosition(canvas);
var offset = position - mousePosition;
mousePosition = position;
Canvas.SetLeft(draggedImage, Canvas.GetLeft(draggedImage) + offset.X);
Canvas.SetTop(draggedImage, Canvas.GetTop(draggedImage) + offset.Y);
}
}
but this didnt work saddly, does anyone has an Idea on how to make it work?

You should not mix layout (i.e. set Canvas.Left/Top) and render transformations. If you zoom by means of a RenderTransform, you should also pan by the same RenderTransform.
Here is a simple example where the MouseMove handler also manipulates the RenderTransform of the Image element.
<Canvas x:Name="canvas"
Background="Transparent"
MouseLeftButtonDown="CanvasMouseLeftButtonDown"
MouseLeftButtonUp="CanvasMouseLeftButtonUp"
MouseMove="CanvasMouseMove"
MouseWheel="CanvasMouseWheel">
<Image x:Name="image" Source=...>
<Image.RenderTransform>
<MatrixTransform/>
</Image.RenderTransform>
</Image>
</Canvas>
Note that mouse positions are determined differently for pan and zoom, in order to get an "unscaled" offset for panning, but an origin point relative to the Image for zooming.
private Point? mousePosition;
private void CanvasMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (canvas.CaptureMouse())
{
mousePosition = e.GetPosition(canvas); // position in Canvas
}
}
private void CanvasMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
canvas.ReleaseMouseCapture();
mousePosition = null;
}
private void CanvasMouseMove(object sender, MouseEventArgs e)
{
if (mousePosition.HasValue)
{
var position = e.GetPosition(canvas); // position in Canvas
var translation = position - mousePosition.Value;
mousePosition = position;
var transform = (MatrixTransform)image.RenderTransform;
var matrix = transform.Matrix;
matrix.Translate(translation.X, translation.Y);
transform.Matrix = matrix;
}
}
private void CanvasMouseWheel(object sender, MouseWheelEventArgs e)
{
var position = e.GetPosition(image); // position in Image
var scale = e.Delta >= 0 ? 1.1 : (1.0 / 1.1);
var transform = (MatrixTransform)image.RenderTransform;
var matrix = transform.Matrix;
matrix.ScaleAtPrepend(scale, scale, position.X, position.Y);
transform.Matrix = matrix;
}

Related

Create a Circle by MouseDown and moved it around

i write a little application in C# with wpf. My goal is to draw a circle in a picture. As long as the mouse button is pressed, the circle should be movable and only after the user has released the mouse button the circle should be finally drawn. For drawing the ellipse i use DrawEllipse.
grf.DrawEllipse(
myPen,
(float)xOriginal - 25,
(float)yOriginal - 25,
radius,
radius
);
After the mouse is released, the circle should be drawn. Then I would like to pick up the coordinates and save.
My idea is to use MouseDown, MouseMove and MouseUp. MouseDown registers the click. With MouseMove, the circles should be redrawn each time and with MouseUp, the circle should be finally drawn.
My problem is that with MouseMove the circle is drawn again and again and not deleted. In addition, it is incredibly delayed. Is there a better solution
Here is my quick and dirty code snippet:
bool registerClick = false;
private void Image_imageBox_MouseregisterClick(object sender, MouseButtonEventArgs e)
{
registerClick = true;
}
private void Image_imageBox_MouseMove(object sender, MouseEventArgs e)
{
if (registerClick)
{
Pen myPen = new Pen(Color.FromArgb(255, 0, 0, 0), 10);
int radius = 50;
Bitmap b1 = _detektion.BildOriginal.Bitmap;
using (Graphics grf = Graphics.FromImage(b1))
{
// zeichne denkreis ein
grf.DrawEllipse(
myPen,
((float)e.GetPosition(imageBox_Image).X - 25,
(float)e.GetPosition(imageBox_Image).X - 25,
radius,
radius
);
}
imageBox_Image.Source = DGX_Body.Utility.Images.ConvertBitmapToBitmapImage(b1);
}
}
private void Image_imageBox_MouseUp(object sender, MouseButtonEventArgs e)
{
Console.WriteLine("Up!");
registerClick = false;
}
Can you help me please.
Thank you
Here is a very basic example of Ellipses in a Canvas with mouse input.
XAML:
<Grid>
<Image Source="C:\Users\Public\Pictures\Sample Pictures\Koala.jpg"
Stretch="None" HorizontalAlignment="Left" VerticalAlignment="Top"/>
<Canvas Background="Transparent"
MouseLeftButtonDown="CanvasMouseLeftButtonDown"
MouseLeftButtonUp="CanvasMouseLeftButtonUp"
MouseMove="CanvasMouseMove"/>
</Grid>
Code behind with event handlers:
private Ellipse currentEllipse;
private void CanvasMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
var canvas = (Canvas)sender;
var pos = e.GetPosition(canvas);
canvas.CaptureMouse();
currentEllipse = new Ellipse
{
Width = 50,
Height = 50,
Margin = new Thickness(-25, -25, 0, 0),
Stroke = Brushes.White,
StrokeThickness = 3
};
Canvas.SetLeft(currentEllipse, pos.X);
Canvas.SetTop(currentEllipse, pos.Y);
canvas.Children.Add(currentEllipse);
}
private void CanvasMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
var canvas = (Canvas)sender;
canvas.ReleaseMouseCapture();
currentEllipse = null;
}
private void CanvasMouseMove(object sender, MouseEventArgs e)
{
if (currentEllipse != null)
{
var canvas = (Canvas)sender;
var pos = e.GetPosition(canvas);
Canvas.SetLeft(currentEllipse, pos.X);
Canvas.SetTop(currentEllipse, pos.Y);
}
}
With this small change you may also pick existing Ellipses:
private void CanvasMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
var canvas = (Canvas)sender;
var pos = e.GetPosition(canvas);
canvas.CaptureMouse();
currentEllipse = e.OriginalSource as Ellipse;
if (currentEllipse == null)
{
currentEllipse = new Ellipse
{
Width = 50,
Height = 50,
Margin = new Thickness(-25, -25, 0, 0),
Fill = Brushes.Transparent,
Stroke = Brushes.White,
StrokeThickness = 3
};
canvas.Children.Add(currentEllipse);
}
Canvas.SetLeft(currentEllipse, pos.X);
Canvas.SetTop(currentEllipse, pos.Y);
}

rectangle drawn in different point when canvas is zoomed

I have a canvas which has a zooming function. There are a lot of elements inside of it so, for selection I was going to use a selection box which I create dynamically when selection box is clicked. On clicking the button, I add the rectangle to the canvas and again, on clicking the button, I remove it.
I have the following xaml code:
<Viewbox x:Name="vbCanvas">
<Grid x:Name="theGrid"
MouseDown="Grid_MouseDown"
MouseUp="Grid_MouseUp"
MouseMove="Grid_MouseMove"
Background="Transparent">
<Canvas Name="canvasWaSNA" Margin="0,10,10,10" Height="720" Width="1280">
</Canvas>
</Grid>
</Viewbox>
mouse events of theGrid draws the rectangle on runtime on the canvas. The codes for those events are:
bool mouseDown = false;
Point mouseDownPos;
Point mouseUpPos;
private void Grid_MouseDown(object sender, MouseButtonEventArgs e)
{
mouseDown = true;
mouseDownPos = e.GetPosition(theGrid);
theGrid.CaptureMouse();
// Initial placement of the drag selection box.
Canvas.SetLeft(sBox, mouseDownPos.X);
Canvas.SetTop(sBox, mouseDownPos.Y);
sBox.Width = 0;
sBox.Height = 0;
// Make the drag selection box visible.
sBox.Visibility = Visibility.Visible;
}
}
private void Grid_MouseUp(object sender, MouseButtonEventArgs e)
{
// Release the mouse capture and stop tracking it.
mouseDown = false;
mouseUpPos = e.GetPosition(theGrid);
theGrid.ReleaseMouseCapture();
// Show the drag selection box.
sBox.Visibility = Visibility.Visible;
MessageBox.Show(mouseDownPos.ToString() + " " + mouseUpPos.ToString());
}
private void Grid_MouseMove(object sender, MouseEventArgs e)
{
if (mouseDown)
{
// When the mouse is held down, reposition the drag selection box.
Point mousePos = e.GetPosition(theGrid);
if (mouseDownPos.X < mousePos.X)
{
Canvas.SetLeft(sBox, mouseDownPos.X);
sBox.Width = mousePos.X - mouseDownPos.X;
}
else
{
Canvas.SetLeft(sBox, mousePos.X);
sBox.Width = mouseDownPos.X - mousePos.X;
}
if (mouseDownPos.Y < mousePos.Y)
{
Canvas.SetTop(sBox, mouseDownPos.Y);
sBox.Height = mousePos.Y - mouseDownPos.Y;
}
else
{
Canvas.SetTop(sBox, mousePos.Y);
sBox.Height = mouseDownPos.Y - mousePos.Y;
}
}
}
To create a Rectangle at runtime, I have to click a button. The event of that button is as follows:
private void select_Click_1(object sender, RoutedEventArgs e)
{
if (!canvasWaSNA.Children.Contains(sBox))
{
sBox.Name = "selectionBox";
sBox.StrokeThickness = 1.5 / zoomfactor;
sBox.StrokeDashArray = new DoubleCollection { 1, 2 };
sBox.Visibility = System.Windows.Visibility.Collapsed;
sBox.Stroke = Brushes.Gray;
canvasWaSNA.Children.Add(sBox);
}
else
{
sBox.Visibility = System.Windows.Visibility.Collapsed;
canvasWaSNA.Children.Remove(sBox);
}
}
I am using the following code to zoom into the canvas:
double zoomfactor = 1.0;
void window_MouseWheel(object sender, MouseWheelEventArgs e)
{
Point p = e.MouseDevice.GetPosition(canvasWaSNA); //gets the location of the canvas at which the mouse is pointed
Matrix m = canvasWaSNA.RenderTransform.Value;
if (e.Delta > 0)
{ //the amount of wheel of mouse changed. e.Delta holds int value.. +ve for uproll and -ve for downroll
m.ScaleAtPrepend(1.1, 1.1, p.X, p.Y);
zoomfactor *= 1.1;
}
else
{
m.ScaleAtPrepend(1 / 1.1, 1 / 1.1, p.X, p.Y);
zoomfactor /= 1.1;
}
canvasWaSNA.RenderTransform = new MatrixTransform(m);
}
When my canvas is on original size, The rectangle is drawn perfectly but as I zoom in or zoom out, rectangle is drawn abnormally. It starts to draw from other points. What might be the problem? Please help
Well, I wasnot supposed to capture the mouse position with respect to theGrid, as I had to create the rectangle with respect to canvas. So, I have to get the position as e.GetPosition(canvasWaSNA) and the intended result was shown. It captured the mouse position on the canvas. Now, the rectangle is drawn perfectly even when zoomed in or zoomed out.
Also, I improved the StrokeThickness of the rectangle drawn by referencing it with the zoomfactor of the canvas.
private void Grid_MouseDown(object sender, MouseButtonEventArgs e)
{mouseDown = true;
mouseDownPos = e.GetPosition(canvasWaSNA);
theGrid.CaptureMouse();
sBox.StrokeThickness = 1.5 / zoomfactor;
// Initial placement of the drag selection box.
Canvas.SetLeft(sBox, mouseDownPos.X);
Canvas.SetTop(sBox, mouseDownPos.Y);
sBox.Width = 0;
sBox.Height = 0;
// Make the drag selection box visible.
sBox.Visibility = Visibility.Visible;
}

Move an image over a canvas in windows store app

I need to move an image over a canvas on tap and slide. How can I achieve this. I tried the following. The image is moving but not as the user moves it.
XAML
<Canvas Background="White">
<Image Name="img" Width="200" Height="200" Source="Assets/11.png" ManipulationMode="All" ManipulationStarted="img_ManipulationStarted" ManipulationDelta="img_ManipulationDelta"/>
</Canvas>
C#
private Point initialPt;
private void img_ManipulationStarted(object sender, ManipulationStartedRoutedEventArgs e)
{
initialPt = e.Position;
}
private void img_ManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e)
{
Point currentPt = e.Position;
double x = currentPt.X - initialPt.X;
double y = currentPt.Y - initialPt.Y;
if (x != 0 || y != 0)
{
TranslateTransform posTransform = new TranslateTransform();
posTransform.X = currentPt.X;
posTransform.Y = currentPt.Y;
img.RenderTransform = posTransform;
e.Complete();
}
}
Instead of using a TranslateTransform, you should directly set the absolute position in the canvas, so you have to bind the ManipulationDelta event to the Canvas, and detect if the point of impact is inside of the image.
<Canvas Background="White" ManipulationMode="All" ManipulationDelta="canvas_ManipulationDelta">
<Image Name="img" Width="200" Height="200" Source="Assets/11.png"/>
</Canvas>
Here is the new event handling function:
private void canvas_ManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e)
{
if ((e.Position.X > Canvas.GetLeft(img) && e.Position.X < Canvas.GetLeft(img) + img.Width)
|| (e.Position.Y > Canvas.GetTop(img) && e.Position.Y < Canvas.GetTop(img) + img.Height)) {
{
Canvas.SetLeft(img, e.Position.X);
Canvas.SetTop(img, e.Position.Y);
}
}
Simple as pie. You can remove initialPt and img_ManipulationStarted.

Getting pinch-to-zoom x/y scale values independent of each other in Windows Store Apps

How do I get the pinch-to-zoom x and y scaling values independent of each other for a Windows Store App? I'm currently using ManipulationDeltaRoutedEventArgs's ManipulationDelta structure, but as you can see it only offers a single scale.
// Global Transform used to change the position of the Rectangle.
private TranslateTransform dragTranslation;
private ScaleTransform scaleTransform;
// Constructor
public MainPage()
{
InitializeComponent();
// Add handler for the ManipulationDelta event
TestRectangle.ManipulationDelta += Drag_ManipulationDelta;
dragTranslation = new TranslateTransform();
scaleTransform = new ScaleTransform();
TestRectangle.RenderTransform = this.dragTranslation;
}
void Drag_ManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e)
{
// Move the rectangle.
dragTranslation.X += e.Delta.Translation.X;
dragTranslation.Y += e.Delta.Translation.Y;
// Scaling, but I want X and Y independent!
scaleTransform.ScaleX = e.Delta.Scale;
scaleTransform.ScaleY = e.Delta.Scale;
}
XAML:
<Rectangle Name="TestRectangle"
Width="200" Height="200" Fill="Blue"
ManipulationMode="All"/>
Code mostly taken from here.
I ended up using Handling Two, Three, Four Fingers Swipe Gestures in WinRT App to grab the coordinates of the two fingers, calculated the initial difference between them, and then scaled accordingly as the distance changed.
int numActiveContacts;
Dictionary<uint, int> contacts;
List<PointF> locationsOfSortedTouches;
void myCanvas_PointerPressed(object sender, PointerRoutedEventArgs e) {
PointerPoint pt = e.GetCurrentPoint(myCanvas);
locationsOfSortedTouches.Add(new PointF((float) pt.Position.X, (float) pt.Position.Y));
touchHandler.TouchesBegan(locationsOfSortedTouches);
contacts[pt.PointerId] = numActiveContacts;
++numActiveContacts;
e.Handled = true;
}
void myCanvas_PointerMoved(object sender, PointerRoutedEventArgs e) {
var pt = e.GetCurrentPoint(myCanvas);
var ptrId = pt.PointerId;
if (contacts.ContainsKey(ptrId)) {
var ptrOrdinal = contacts[ptrId];
Windows.Foundation.Point currentContact = pt.Position;
locationsOfSortedTouches[ptrOrdinal] = new PointF((float) pt.Position.X, (float) pt.Position.Y);
//distance calculation and zoom redraw here
}
e.Handled = true;
}

Clipping InkPresenter to drawing area

I am trying to capture a signature in windows phone 7.1.
I can draw on the screen yet I can not limit the drawing area to the InkPresenter control except by adding some handling in the mousemove event.
How can I limit the drawing area using XAML or is this not possible?
XAML Code
<InkPresenter Name="inkTest" Background="White" MinHeight="180" MinWidth="250" />
Code Behind
private Stroke _currentStroke;
private void inkTest_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
_currentStroke = null;
}
private void inkTest_MouseMove(object sender, MouseEventArgs e)
{
if (_currentStroke == null) return;
//HACK: want to set this in XAML
var position = e.GetPosition(inkTest);
if (position.X <= inkTest.ActualWidth &&
position.Y <= inkTest.ActualHeight)
_currentStroke.StylusPoints.Add(GetStylusPoint(position));
}
private void inkTest_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
inkTest.CaptureMouse();
_currentStroke = new Stroke();
_currentStroke.StylusPoints.Add(GetStylusPoint(e.GetPosition(inkTest)));
_currentStroke.DrawingAttributes.Color = Colors.Blue;
inkTest.Strokes.Add(_currentStroke);
}
private StylusPoint GetStylusPoint(Point position)
{
return new StylusPoint(position.X, position.Y);
}
Untested, but try clipping:
<InkPresenter Name="inkTest" Background="White" MinHeight="180" MinWidth="250">
<InkPresenter.Clip>
<RectangleGeometry Rect="0,0,180,250"/>
</InkPresenter.Clip>
</InkPresenter>
Change the boundaries of the RectangleGeometry to what you want (or change the RectangleGeometry element itself if you need a different shape).

Categories