I have a problem with moving the button, which is that it follows the button with the cursor, then when the left mouse button up it has a way to adjust to the correct position. Unfortunately, when I up the left button, the control does not improve its position, it just goes back to the previous position and when I point at it, it suddenly changes its position to a different one, and in this way it runs away.
XAML
<Grid x:Name="GridMain" x:FieldModifier="public">
<Button MouseLeftButtonDown="Button_MouseLeftButtonDown" MouseLeftButtonUp="Button_MouseLeftButtonUp" MouseMove="Button_MouseMove"></Button>
<Button MouseLeftButtonDown="Button_MouseLeftButtonDown" MouseLeftButtonUp="Button_MouseLeftButtonUp" MouseMove="Button_MouseMove"></Button>
<Button MouseLeftButtonDown="Button_MouseLeftButtonDown" MouseLeftButtonUp="Button_MouseLeftButtonUp" MouseMove="Button_MouseMove"></Button>
<Button MouseLeftButtonDown="Button_MouseLeftButtonDown" MouseLeftButtonUp="Button_MouseLeftButtonUp" MouseMove="Button_MouseMove"></Button>
</Grid>
C#
bool _isMoving;
System.Windows.Point? _buttonPosition;
System.Windows.Point relativePoint;
double deltaX;
double deltaY;
TranslateTransform _currentTT;
Button selectedButton;
private void Button_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (_buttonPosition == null && selectedButton = null)
{
_buttonPosition = this.TransformToAncestor(MainWindow.mainWindow.GridMain).Transform(new System.Windows.Point(0, 0));
selectedButton = (sender as Button);
}
var mousePosition = Mouse.GetPosition(MainWindow.mainWindow.GridMain);
deltaX = mousePosition.X - _buttonPosition.Value.X;
deltaY = mousePosition.Y - _buttonPosition.Value.Y;
_isMoving = true;
}
private void Button_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
_isMoving = false;
System.Windows.Point relativePoint = this.TransformToAncestor(MainWindow.mainWindow.GridMain).Transform(new System.Windows.Point(0, 0));
selectedButton.RenderTransform = new TranslateTransform(Math.Floor(relativePoint.X / 2), Math.Floor(relativePoint.Y / 45));
}
private void Button_MouseMove(object sender, MouseEventArgs e)
{
if (!_isMoving) return;
var mousePoint = Mouse.GetPosition(MainWindow.mainWindow.GridMain);
var offsetX = (_currentTT == null ? _buttonPosition.Value.X : _buttonPosition.Value.X - _currentTT.X) + deltaX - mousePoint.X;
var offsetY = (_currentTT == null ? _buttonPosition.Value.Y : _buttonPosition.Value.Y - _currentTT.Y) + deltaY - mousePoint.Y;
selectedButton.RenderTransform = new TranslateTransform(-offsetX, -offsetY);
}
Related
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;
}
I am trying to make a UserControl draggable and I'm stuck and I have no idea why.
Basing myself on the answer here: How to drag a UserControl inside a Canvas
This portion of code is where I'm stuck:
private void Control_MouseMove(object sender, MouseEventArgs e)
{
var userControl= sender as UserControl;
Point currentPosition = e.GetPosition(userControl);
var transform = userControl.RenderTransform as TranslateTransform;
if (transform == null)
{
transform = new TranslateTransform();
userControl.RenderTransform = transform;
}
transform.X = currentPosition.X - clickPosition.X;
transform.Y = currentPosition.Y - clickPosition.Y;
}
}
What happens is that while dragging, the control jumps back and forth from its new position to it's old position.
When I change the above code to:
Point currentPosition = e.GetPosition(null);
The dragging works without the jumping back to the original position, but it is obviously offset. The UserControl in question does not have a parent control (in the sense that this.Parent is null).
The solution might be obvious but I'm just not seeing it. Any ideas?
Use LayoutTransform. RenderTransform only makes the UIElement "looks like" it is at the new position. This means that the first time the event triggers (first move), it is working as intended, but thereafter, the position is still at the original position, but it's being render-transformed to look like it has moved - this makes your move event go crazy.
It's still better to wrap your control in a Canvas, in my opinion. Using LayoutTransform is usually not recommended.
Step1: Add the Properties in UserControl
Point _anchorPoint;
Point _currentPoint;
bool _isInDrag;
private readonly TranslateTransform _transform = new TranslateTransform();
Step 2: Initialize your Constructor with following EventHandlers
this.MouseLeftButtonDown += new MouseButtonEventHandler(Control_MouseLeftButtonDown);
this.MouseLeftButtonUp += new MouseButtonEventHandler(Control_MouseLeftButtonUp);
this.MouseMove += new MouseEventHandler(Control_MouseMove);
3.Implement the Event Methods
private void Control_MouseMove(object sender, MouseEventArgs e)
{
if (!_isInDrag) return;
_currentPoint = e.GetPosition(null);
//This is the change to the position that we want to apply
Point delta = new Point();
delta.X = _currentPoint.X - _anchorPoint.X;
delta.Y = _currentPoint.Y - _anchorPoint.Y;
//Calculate user control edges
var leftEdge = Margin.Left + _transform.X + delta.X;
var topEdge = Margin.Top + _transform.Y + delta.Y;
var rightEdge = Width + Margin.Left + _transform.X + delta.X;
var bottomEdge = Height + Margin.Top + _transform.Y + delta.Y;
//Set the delta to 0 if it goes over _parentGrid edges
if (leftEdge < 0) delta.X = 0;
if (topEdge < 0) delta.Y = 0;
//_ParentGridName is called from MDIParent.GridName
if (rightEdge > _ParentGridName.Width) delta.X = 0;
if (bottomEdge > _ParentGridName.Height) delta.Y = 0;
//Apply the delta to the user control
_transform.X += delta.X;
_transform.Y += delta.Y;
RenderTransform = _transform;
_anchorPoint = _currentPoint;
}
private void Control_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (_isInDrag)
{
var element = sender as FrameworkElement;
element.ReleaseMouseCapture();
_isInDrag = false;
e.Handled = true;
}
}
private void Control_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
var element = sender as FrameworkElement;
_anchorPoint = e.GetPosition(null);
if (element != null) element.CaptureMouse();
_isInDrag = true;
e.Handled = true;
}
I have a user control that I am dragging inside of a grid. The Z-Index is set pretty high so that I can keep it above the other children. Dragging the control works perfectly, but if a user wants to move the control outside of the grid it will allow it.
How do I keep it from leaving the bounds of the parent Grid control, here is what I have now:
private System.Windows.Point _anchorPoint;
private System.Windows.Point _currentPoint;
private bool _isInDrag;
private void UserControl_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
var element = sender as FrameworkElement;
_anchorPoint = e.GetPosition(null);
if (element != null) element.CaptureMouse();
_isInDrag = true;
e.Handled = true;
}
private void UserControl_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (!_isInDrag) return;
var element = sender as FrameworkElement;
if (element != null) element.ReleaseMouseCapture();
_isInDrag = false;
e.Handled = true;
}
private void UserControl_MouseMove(object sender, MouseEventArgs e)
{
if (!_isInDrag) return;
_currentPoint = e.GetPosition(null);
UIElement container = VisualTreeHelper.GetParent(_parentGrid) as UIElement;
System.Windows.Point relativeLocation = _parentGrid.TranslatePoint(new System.Windows.Point(0, 0), container);
if (_currentPoint.X > relativeLocation.X) return;
if(_currentPoint.Y >= relativeLocation.Y)return;
_transform.X += _currentPoint.X - _anchorPoint.X;
_transform.Y += (_currentPoint.Y - _anchorPoint.Y);
RenderTransform = _transform;
_anchorPoint = _currentPoint;
}
The "relativeLocation" is always 0x0, so thats not working. Any ideas would greatly be appreciated.
*Note : I know if I changed my UserControl to a Window it would mitigate all of the issues that I am having. But to be honest, it looks great this way and I really don't want to clutter the window up. This system opens up as a dashboard that consumes the user's' entire window ( is opened on a separate window). So when you open a window here, its doesn't flow right.
I don't think you need the relativeLocation. The math can be a bit annoying to get right, though. Try this approach, it worked well when I tested it:
private void UserControl_MouseMove(object sender, MouseEventArgs e)
{
if (!_isInDrag) return;
_currentPoint = e.GetPosition(null);
//This is the change to the position that we want to apply
Point delta = new Point();
delta.X = _currentPoint.X - _anchorPoint.X;
delta.Y = _currentPoint.Y - _anchorPoint.Y;
//Calculate user control edges
var leftEdge = Margin.Left + _transform.X + delta.X;
var topEdge = Margin.Top + _transform.Y + delta.Y;
var rightEdge = Width + Margin.Left + _transform.X + delta.X;
var bottomEdge = Height + Margin.Top + _transform.Y + delta.Y;
//Set the delta to 0 if it goes over _parentGrid edges
if (leftEdge < 0) delta.X = 0;
if (topEdge < 0) delta.Y = 0;
if (rightEdge > _parentGrid.Width) delta.X = 0;
if (bottomEdge > _parentGrid.Height) delta.Y = 0;
//Apply the delta to the user control
_transform.X += delta.X;
_transform.Y += delta.Y;
RenderTransform = _transform;
_anchorPoint = _currentPoint;
}
This is a start:
Point position = _parentGrid.PointToScreen(new Point(0, 0));
PresentationSource source = PresentationSource.FromVisual(_parentGrid);
position = source.CompositionTarget.TransformFromDevice.Transform(position);
Now you have the screen coordinates of the parent grid. The call to Transform() is important as it will convert pixels to WPF device independent pixels, matching the system DPI setting.
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 can successfully move a line around the window in wpf but it's not working properly.
it's like if the cursor is way fast that the line it's dragging.
you can test the code to see if you can find what's the problem on it.
public partial class MainWindow : Window
{
Line line = new Line();
Point p ;
bool isdragging = false;
public MainWindow()
{
InitializeComponent();
canvas1.Children.Add(line);
Thickness thickness = new Thickness(101, -11, 362, 250);
line.Margin = thickness;
line.Visibility = System.Windows.Visibility.Visible;
line.StrokeThickness = 4;
line.Stroke = System.Windows.Media.Brushes.Black;
line.X1 = 10;
line.X2 = 200;
line.Y1 = 0;
line.Y2 = 70;
line.MouseDown+=new MouseButtonEventHandler(line_MouseDown);
line.MouseMove+=new MouseEventHandler(line_MouseMove);
line.MouseUp+=new MouseButtonEventHandler(line_MouseUp);
}
public void line_MouseDown(object sender, MouseButtonEventArgs e)
{
isdragging = true;
p = e.GetPosition(canvas1);
}
public void line_MouseMove(object sender, MouseEventArgs e)
{
if (isdragging == true && e.LeftButton == MouseButtonState.Pressed)
{
line.X1 += e.GetPosition(canvas1).X - p.X;
line.X2 += e.GetPosition(canvas1).X - p.X;
line.Y1 += e.GetPosition(canvas1).Y - p.Y;
line.Y2 += e.GetPosition(canvas1).Y - p.Y;
}
}
public void line_MouseUp(object sender, MouseButtonEventArgs e)
{
isdragging = false;
}
}
}
If you want really smooth dragging you could wrap the Line in a Thumb so you can use the DragDelta event to calculate the new position.
Example:
Xaml:
<Window x:Class="WpfApplication8.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="233" Width="405" Name="UI">
<Canvas>
<Thumb DragDelta="onDragDelta" Canvas.Left="0" Canvas.Top="0" >
<Thumb.Template>
<ControlTemplate>
<Line X1="10" X2="200" Y1="0" Y2="70" StrokeThickness="4" Stroke="Black"/>
</ControlTemplate>
</Thumb.Template>
</Thumb>
</Canvas>
</Window>
Code:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
void onDragDelta(object sender, DragDeltaEventArgs e)
{
var thumb = sender as Thumb;
Canvas.SetLeft(thumb, Canvas.GetLeft(thumb) + e.HorizontalChange);
Canvas.SetTop(thumb, Canvas.GetTop(thumb) + e.VerticalChange);
}
}
You are moving the line after every mouse move event. This will cause the code to attempt to redraw the line in it's new location after every slightest move of the mouse - even down to a single pixel.
You could keep a track of the position and only redraw the line if the mouse has moved more than a certain amount:
public void line_MouseMove(object sender, MouseEventArgs e)
{
if (isdragging == true && e.LeftButton == MouseButtonState.Pressed)
{
Point newPos = e.GetPosition(canvas1);
if (Size(p, newPos) > 10)
{
line.X1 += newPos.X - p.X;
line.X2 += newPos.X - p.X;
line.Y1 += newPos.Y - p.Y;
line.Y2 += newPos.Y - p.Y;
p = newPos;
}
}
}
Where Size is a method that calculates the distance from p to newPos.
Then you just need to add a final update when the mouse is released so that the line ends up where the user expects it to be:
public void line_MouseUp(object sender, MouseButtonEventArgs e)
{
Point newPos = e.GetPosition(canvas1);
line.X1 += newPos.X - p.X;
line.X2 += newPos.X - p.X;
line.Y1 += newPos.Y - p.Y;
line.Y2 += newPos.Y - p.Y;
isdragging = false;
}
Thank you guys for you help!!
i think i solved that problem buy capturing the mouse when i'm m and i release the mouse capture after releasing the button of the mouse.
it works just fine.
thank you again guys!