TranslateTransform on a WPF Slidercontrol - c#

I am trying to make it so that when the user holds down the left mouse button the slider control appears where the mouse is positioned and the user can move the slider control left/right. Once the left mouse button is removed it will pass the value into a method. I am completely stuck on this. I have the slider control built, with a default position (the position can vary).
I have tried the following code to get the slider control to move to the mouses position but it does not do anything:
private void Button_Click(object sender, RoutedEventArgs e)
{
TranslateTransform currentTransform = new TranslateTransform();
currentTransform = slider.RenderTransform as TranslateTransform;
currentTransform.X = Mouse.GetPosition(Deck_Door).X;
currentTransform.Y = Mouse.GetPosition(Deck_Door).Y;
slider.Visibility = Visibility.Visible;
}
Thanks in advance!

First you should capture mouse
then you should calculate mouse move distance
protected override void OnPreviewMouseLeftButtonDown(MouseButtonEventArgs e)
{
this.CaptureMouse();
//Show slider here
}
protected override void OnPreviewMouseLeftButtonUp(MouseButtonEventArgs e)
{
this.ReleaseMouseCapture();
//Hide slider here and get it's value
}
Point previousMousePosition;
protected override void OnPreviewMouseMove(MouseEventArgs e)
{
if (this.IsMouseCaptured)
{
Point point = e.GetPosition(this);
double d = point.X - previousMousePosition.X;
//you can use this value to change the slider's value
this.previousMousePosition = point;
}
}

I was able to do this fairly easily. I modified the slider code to include a name for the translateTransform:
<Slider x:Name="slider"
TickFrequency="1"
Value="1"
IsSnapToTickEnabled="True"
IsMoveToPointEnabled="True"
Minimum="0"
Maximum="10"
ValueChanged="slider_ValueChanged"
AutoToolTipPlacement="BottomRight"
Grid.Column="0" VerticalAlignment="Top" Margin="0,-3,51.5,0"
Thumb.DragCompleted="slider_DragCompleted" >
<Slider.RenderTransform>
<TranslateTransform x:Name="mySliderTransform" />
</Slider.RenderTransform>
</Slider>
Then I hooked a click event to a button and used the following code:
private void Button_Click(object sender, RoutedEventArgs e)
{
slider.Visibility = Visibility.Visible;
slider.Value = 1;
// get the mouse positions
string x = Mouse.GetPosition(this).X.ToString();
string y = Mouse.GetPosition(this).Y.ToString();
// convert the mouse position to a double
var X = Convert.ToDouble(x);
var Y = Convert.ToDouble(y);
// reset the slider transform and apply the coordinates of the mouse position.
mySliderTransform.X = 0;
mySliderTransform.Y = 0;
mySliderTransform.X = X - 20;
mySliderTransform.Y = Y;
}

Related

How to make UIElement's location transform persistent?

While looking for ways to drag around a UIElement in WPF I came across some code and have been experimenting with it. When the Element is clicked, it nicely follows the mouse, but on a subsequent drag-event, the Element reset itself to its original position.
The xaml setup: Very simple, just a named Canvas with the most original name ever and the Element, in this case a grid, called Tile1.
<Grid>
<Canvas x:Name="Canvas" Width="200" Height="300" Background="LightGray">
<Grid x:Name="Tile1">
<Border BorderBrush="Black" BorderThickness="1" Background="White">
<Control Width="25" Height="25"/>
</Border>
</Grid>
</Canvas>
</Grid>
some code-behind:
public TranslateTransform transPoint;
public Point originPoint;
public MainWindow()
{
InitializeComponent();
Tile1.MouseLeftButtonDown += Tile1_MouseLeftButtonDown;
Tile1.MouseLeftButtonUp += Tile1_MouseLeftButtonUp;
Tile1.MouseMove += Tile1_MouseMove;
}
private void Tile1_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
var myLocation = e.GetPosition(Canvas);
originPoint = new Point(myLocation.X, myLocation.Y);
transPoint = new TranslateTransform(originPoint.X, originPoint.Y);
}
private void Tile1_MouseMove(object sender, MouseEventArgs e)
{
var mouseLocation = e.GetPosition(Canvas);
if (e.LeftButton == MouseButtonState.Pressed)
{
transPoint.X = (mouseLocation.X - originPoint.X);
transPoint.Y = (mouseLocation.Y - originPoint.Y);
Tile1.RenderTransform = transPoint;
}
}
private void Tile1_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
var mouseLocationOnCanvas = e.GetPosition(Canvas);
var mouseLocationOnTile = e.GetPosition(Tile1);
//attempting to account for offset of mouse on the Tile:
var newX = mouseLocationOnCanvas.X - mouseLocationOnTile.X;
var newY = mouseLocationOnCanvas.Y - mouseLocationOnTile.Y;
Tile1.Margin = new Thickness(newX, newY, 0, 0);
}
In the original example (reference added here) A MouseUpEvent wasn't even used. Without it, my element just resets to its original position at 0,0 at every MouseDragEvent. And with it, it'll jump all over the place.
My train of thought was to somehow set the Element's current position to where the MouseUpEvent occurred.
I've been fiddling around with different things, as this particular stuff is rather new to me. Examples are: Tile1.TransformToAncestor(Canvas).Transform(mouseLocation);
I also found that VisualOffset has the information I need, so somehow it's already stored on the object, before being reset, but I haven't found a way to access it in any form.
Tile1.SetValue(VisualOffset.X = ...); or Tile1Grid.GetValue(VisualOffset);
So basically, is there a way to not have the element reset its position after RenderTransform?
I didn't find MouseLeftButtonDown event useful so I removed it:
I noticed some lags when moving the mouse that ould be annoying maybe the event needs to run asynchronously MouseMove performance slow using GetPosition.
public TranslateTransform transPoint = new TranslateTransform(0, 0);
public Point originPoint = new Point(0, 0);
public MainWindow()
{
InitializeComponent();
Tile1.MouseLeftButtonUp += Tile1_MouseLeftButtonUp;
Tile1.MouseMove += Tile1_MouseMove;
}
private void Tile1_MouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed)
{
var mouseLocation = e.GetPosition(Canvas);
transPoint.X = (mouseLocation.X - originPoint.X);
transPoint.Y = (mouseLocation.Y - originPoint.Y);
Tile1.RenderTransform = transPoint;
}
}
private void Tile1_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
var mouseLocation = e.GetPosition(Tile1);
originPoint.X = mouseLocation.X;
originPoint.Y = mouseLocation.Y;
}
The RenderTransform seems to erratic, but I got the UIElement to stay put after moving it using the following:
private void MovableTile_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
tile.CaptureMouse();
}
with
private void MovableTile_MouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed)
{
var tile = sender as UIElement;
var mousePosition = e.GetPosition(canvas);
Canvas.SetLeft(tile, mousePosition.X);
Canvas.SetTop(tile, mousePosition.Y);
}
}
and then
private void MovableTile_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
tile.ReleaseMouseCapture();
}
The MouseCapture was the key here. :)

How to translate and rotate an image

I want to rotate and afterwards move an image in c#. The image is in a Canvas . My problem is if you rotate the image with the following
private void Schiff_OnMouseWheel(object sender, MouseWheelEventArgs e)
{
Image _schiff = (Image)sender;
if (!_schiff.IsMouseCaptured) return;
Matrix _mat = _schiff.RenderTransform.Value;
Point _mouse = e.GetPosition(_schiff);
if (e.Delta > 0)
{
_mat.RotateAtPrepend(22.5, _mouse.X, _mouse.Y);
}
else
{
_mat.RotateAtPrepend(-22.5, _mouse.X, _mouse.Y);
}
MatrixTransform _mtf = new MatrixTransform(_mat);
_schiff.RenderTransform = _mtf;
}
or RotateTransform
double _angle = 0.0;
_angle += 22.5;
if (_angle == 360.0) _angle = 0.0;
RotateTransform _rotate = new RotateTransform(_angle, _schiff.Width / 2, _schiff.Height / 2);
_schiff.RenderTransform = _rotate;A
you just rotate the "picture", but not it's base. So if you want to move the image with Canvas.GetLeft/GetTop, it behaves like it is still not rotated. So if you set the Top/Left-corner, the actual corner of the rotated image isn't placed where I wanted it to be.
At https://wpf.2000things.com/2013/03/08/772-use-rendertransformorigin-to-change-center-point-for-rotation-transforms/, in the picture you can see what I mean. How can I possibly rotate the "base" with the actual image? I saw it is possible in WinForms, but (how) does it work in WPF?
Thanks in advance, if anything is unclear/wrong I will edit my question.
edit:
https://i.stack.imgur.com/tqhKw.png
You can see the two arrows. They are my images. I rotated them at the center with my above MouseWheelEvent. On the right site there is my movement tab. You can change the speed (the checkbox after "Geschwindigkeit") and then you can either turn left in one section (where - is 0°, | is 22.5° and || is 45° and a section is 69 points in the Canvas) or right and end up in a new location.
You should use coordinates relative to the Canvas for transforming the Image element.
With this Image in a Canvas
<Canvas x:Name="canvas">
<Image Width="100"
Source="C:\Users\Public\Pictures\Sample Pictures\Koala.jpg"
MouseLeftButtonDown="OnMouseLeftButtonDown"
MouseLeftButtonUp="OnMouseLeftButtonUp"
MouseMove="OnMouseMove"
MouseWheel="OnMouseWheel">
<Image.RenderTransform>
<MatrixTransform />
</Image.RenderTransform>
</Image>
</Canvas>
the code would look like shown below.
The important part is to use e.GetPosition(canvas) and not to set the child element's Canvas.Left, Canvas.Top and RenderTransformOrigin properties. All transformations are done with a single Matrix in its RenderTransform.
private Point? mousePos;
private void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
((IInputElement)sender).CaptureMouse();
mousePos = e.GetPosition(canvas);
}
private void OnMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
((IInputElement)sender).ReleaseMouseCapture();
mousePos = null;
}
private void OnMouseMove(object sender, MouseEventArgs e)
{
if (mousePos.HasValue)
{
var element = (UIElement)sender;
var transform = (MatrixTransform)element.RenderTransform;
var matrix = transform.Matrix;
var pos = e.GetPosition(canvas);
matrix.Translate(pos.X - mousePos.Value.X, pos.Y - mousePos.Value.Y);
transform.Matrix = matrix;
mousePos = pos;
}
}
private void OnMouseWheel(object sender, MouseWheelEventArgs e)
{
var element = (UIElement)sender;
var transform = (MatrixTransform)element.RenderTransform;
var matrix = transform.Matrix;
var pos = e.GetPosition(canvas);
matrix.RotateAt(e.Delta > 0 ? 22.5 : -22.5, pos.X, pos.Y);
transform.Matrix = matrix;
}

How to draw random/irregular line in XAML canvas?

I have a canvas in XAML in which I want to draw random irregular (Hand drawn) line using C#.
This is what I want: http://jsfiddle.net/GfGVE/9/ HTML Canvas but in XAML C#
I want the line to be drawn Horizontally.
Xaml:
<Canvas Grid.Column="1" x:Name="ItemCanvas"></Canvas>
C#:
public ItemControl()
{
this.InitializeComponent();
canvas = this.ItemCanvas;
}
You need to add methods for the events MouseMove, MouseDown, MouseEnter, MouseUp and MouseLeave. Additionally you need two variables for the current position and the last position. If you also want to undo you need to add a Stack<int> for this. These are the needed methods:
private void Canvas_MouseDown(Object sender, MouseButtonEventArgs e)
{
// push start of line onto undo stack
this.undo.Push(this.paintCanvas.Children.Count);
this.last = e.GetPosition(this.paintCanvas);
}
private void Canvas_MouseMove(Object sender, MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed)
{
if (this.last.X != -1)
{
Point current = e.GetPosition(this.paintCanvas);
Line l = new Line();
l.X1 = this.last.X;
l.Y1 = this.last.Y;
l.X2 = current.X;
l.Y2 = current.Y;
l.Stroke = this.brush;
l.StrokeThickness = this.thickness;
this.paintCanvas.Children.Add(l);
this.last = current;
}
}
}
private void Canvas_MouseUp(Object sender, MouseButtonEventArgs e)
{
// push end of line onto undo stack
this.undo.Push(this.paintCanvas.Children.Count);
}
private void Canvas_MouseLeave(Object sender, MouseEventArgs e)
{
this.last = new Point(-1, -1);
}
private void Canvas_MouseEnter(Object sender, MouseEventArgs e)
{
this.last = e.GetPosition(this.paintCanvas);
}
To remove the last line from your canvas you can call this Undo-method:
private void Undo()
{
if (this.undo.Count == 0)
return;
// pop indexes of last line (start index is one below top of stack)
int to = this.undo.Pop();
int from = this.undo.Pop();
// remove last line from UIElement collection
this.paintCanvas.Children.RemoveRange(from, to);
}
You could use InkCanvas for this purpose. It defines an area that receives and displays ink strokes:
<Grid>
<InkCanvas Name="InkCanvas1"></InkCanvas>
</Grid>
Result:

How do I drag and move an image around inside a Grid in WPF?

How do I drag and move an image around inside a Grid?
Have been trying to solve my problem for days, to no outcome.. here is my code for the xaml.
<Canvas>
<Grid Canvas.Left="134" Canvas.Top="98" Height="500" Width="1010">
<ContentControl x:Name="poolContainer">
</ContentControl>
<Grid DragEnter="Grid_DragEnter" AllowDrop="True">
<Image Canvas.Left="902" Canvas.Top="324" Height="42" Name="CueStick" Visibility="Visible" Source="/NYP_FYPJ_VP2014;component/Images/cue.png" Margin="780,230,-92,228" Drop="CueStick_DragDrop" MouseMove="CueStick_MouseMove" MouseDown="CueStick_MouseDown" MouseUp="CueStick_MouseUp"></Image>
</Grid>
</Grid>
<RepeatButton Canvas.Left="1175" Canvas.Top="397" Content="Rotate" Height="23" Name="buttonUp" Width="74" Click="buttonUp_Click" />
</Canvas>
Here is my xaml.cs code for the image drag
bool drag = false;
int x = 0;
int y = 0;
private bool isPictureReadyToDrag;
private void SetPosition()
{
CueStick.Location = new Point(MousePosition.X - this.Left - CueStick.Width / 2,
MousePosition.Y - this.Top - CueStick.Height);
}
private void CueStick_MouseMove(object sender, MouseEventArgs e)
{
if (isPictureReadyToDrag)
SetPosition();
}
private void CueStick_MouseDown(object sender, MouseButtonEventArgs e)
{
isPictureReadyToDrag = true;
SetPosition();
}
private void CueStick_MouseUp(object sender, MouseButtonEventArgs e)
{
isPictureReadyToDrag = false;
}
You are doing wrong in several places.
The image is put inside a Grid, its position is solely controlled by the Margin property, Canvas.Top/Left do not take effect and you can remove them.
<Image Canvas.Left="202" Canvas.Top="324" Margin="780,230,-92,228"
and in the code behind, set the image's Margin property, not Location (there is no such property).
CueStick.Margin = new Thickness(...
b. Set an explicit Width to the image, because you are using this value in the code behind.
<Image Width="229" Height="42"
c. You are not using the mouse position correctly; you can get it from MouseEventArgs/MouseButtonEventArgs, something like
private void CueStick_MouseDown(object sender, MouseButtonEventArgs e)
{
isPictureReadyToDrag = true;
double x = e.GetPosition(grid1).X;
double y = e.GetPosition(grid1).Y;
SetPosition(x, y);
}
private void SetPosition(double x, double y)
{
CueStick.Margin = new Thickness(x - CueStick.Width / 2,
y - CueStick.Height / 2, 0, 0);
}
Noted that grid1 is the containing Grid of the image.
<Grid x:Name="grid1" DragEnter="Grid_DragEnter" AllowDrop="True">
<Image...
The hard work of debugging to get the correct Margin is left to you.

C# Draw polyline(s) on mouse move

I want to draw something on my canvas whenever drawing_mode and is_drawing booleans are on. Right now I am using a polyline list and BackgroundWorker for threading. My main problem is that my code only creates one polyline and the dots are ALWAYS connected. In other words I can stop drawing for a while but then wherever I click a new line connection is made with the previous one. The end result is that my canvas.Children only has one polyline element with all the points. Could anyone help me solve this?
P.S. I am not very good with threading yet...
private BackgroundWorker drawing_worker;
private void drawing_worker_ProgressChanged(object sender, ProgressChangedEventArgs eventargs)
{
Polyline polyline = new Polyline();
polyline.Points = last_polyline.Points;
canvas.Children.Remove(last_polyline);
var pos = canvas_relative_pos;
polyline.Points.Add(pos);
polyline.Stroke = new SolidColorBrush(Colors.Black);
polyline.StrokeThickness = 1;
canvas.Children.Add(polyline);
last_polyline = polyline;
}
private void drawing_worker_DoWork(object sender, DoWorkEventArgs eventargs)
{
while (drawing_mode_enabled && is_drawing)
{
drawing_worker.ReportProgress(0);
Thread.Sleep(5);
if (drawing_worker.CancellationPending) break;
}
}
private void ContentControl_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (drawing_mode_enabled)
{
is_drawing = true;
drawing_worker = new BackgroundWorker
{
WorkerReportsProgress = true,
WorkerSupportsCancellation = true
};
drawing_worker.ProgressChanged += drawing_worker_ProgressChanged;
drawing_worker.DoWork += drawing_worker_DoWork;
drawing_worker.RunWorkerAsync();
}
}
You don't need a BackgroundWorker or any other asynchronous stuff to draw polylines on Canvas.
Just create a Canvas with a Background (so that it gets input events) and handlers for the MouseLeftButtonDown, MouseLeftButtonUp and MouseMove events:
<Canvas Background="Transparent"
MouseLeftButtonDown="CanvasMouseLeftButtonDown"
MouseLeftButtonUp="CanvasMouseLeftButtonUp"
MouseMove="CanvasMouseMove"/>
In the mouse down handler create a new Polyline and add it to the Canvas. Also capture the mouse so that you get mouse move events even when the mouse cursor leaves the Canvas. In the mouse up handler release the mouse capture. Finally, in the mouse move handler, add points to the last polyline child of the Canvas.
private void CanvasMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
var panel = (Panel)sender;
var polyline = new Polyline
{
Stroke = Brushes.Black,
StrokeThickness = 3
};
panel.Children.Add(polyline);
panel.CaptureMouse();
}
private void CanvasMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
((UIElement)sender).ReleaseMouseCapture();
}
private void CanvasMouseMove(object sender, MouseEventArgs e)
{
var panel = (Panel)sender;
if (panel.IsMouseCaptured)
{
var polyline = panel.Children.OfType<Polyline>().Last();
polyline.Points.Add(e.GetPosition(panel));
}
}

Categories

Resources