C# WPF - Virtual joystick using polar coordinates - c#

I am trying to make a virtual joystick that a user can move around with the mouse in WPF in C#. I am trying to use the polar coordinate system because I want the knob of the joystick to remain in the circle.
I am getting some really weird behavior - if anybody has experience with this sort of thing any tips would be nice. Thank you
EDIT: I got it working. I posted the updated code below. It is by no means a good/professional solution to this but it works. So I hope if somebody in the future is trying to do this same task it may help. I tried to add some comments to explain what was going on. Here you go!
NOTE: If you are trying to use this for your program make note that there are two hardcoded values you must change. The first is the x_starting/y_starting. Thats where your virtual joystick knob should reset to. And next is the radius when calculating the max possible value. Make sure it is half the width of the background joystick.
The code:
using System;
using System.Collections.Generic;
using System.Diagnostics;
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 WpfApplication4
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
double radius;
bool captured = false;
double x_shape, x_canvas, y_shape, y_canvas; //Canvas is used to keep track of where the joystick is on screen,
// shape is used for where the knob is.
UIElement source = null;
double y_starting = 180; //The starting X and Y position for the Knob. (CHANGE TO WHERE UR CANVAS.TOP/CANVAS.LEFT IS FOR THE KNOB)
double x_starting = 105;
private void Ellipse_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
string objname = ((Ellipse)sender).Name;
if (objname == "Knob")
{
source = (UIElement)sender;
Mouse.Capture(source);
captured = true;
x_shape = Canvas.GetLeft(reference);
x_canvas = e.GetPosition(Knob).X;
y_shape = Canvas.GetTop(reference);
y_canvas = e.GetPosition(Knob).Y;
}
}
private void Ellipse_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
string objname = ((Ellipse)sender).Name;
Mouse.Capture(null);
captured = false;
if (objname == "Knob") {
// x_shape = x_starting;
// y_shape = y_starting;
Canvas.SetLeft(source, x_starting);
Canvas.SetTop(source, y_starting); //Reset to our starting values
XTextBlock.Text = x_starting.ToString();
YTextBlock.Text = y_starting.ToString();
}
}
private void Ellipse_MouseMove(object sender, MouseEventArgs e)
{
double x = e.GetPosition(reference).X; //Getting mouse pos relative to the center of your joystick (I have an empty textblock there called reference)
double y = e.GetPosition(reference).Y;
double r = Math.Sqrt((x * x) + (y * y)); //Calculate radius..
XMousePos.Text = x.ToString();
YMousePos.Text = y.ToString();
string objname = ((Ellipse)sender).Name;
double theta = Math.Atan2(y, x); //Calculate theta..
Theta.Text = theta.ToString();
double x1 = (r * Math.Cos(theta)); //This converts polar coordinates to cartesian plane coordinates.
double y1 = (r * Math.Sin(theta));
XPolPos.Text = x1.ToString();
YPolPos.Text = y1.ToString();
double xmax = (62.5 * Math.Cos(theta)); //Calculate a max so that your knob stays within the circle. The radius value should be half the width of the
double ymax = (62.5 * Math.Sin(theta)); // background of your joystick.
X2PolPos.Text = xmax.ToString();
Y2PolPos.Text = ymax.ToString();
if (objname == "Knob") {
if (captured)
{
if ((((x1 > 0) && (x1 < xmax)) || ((x1 <= 0) && (x1 > xmax))) && (((y1 > 0) && (y1 < ymax)) || ((y1 <= 0) && (y1 > ymax)))) //Seems like bad way to do it. But this is how i check to see if knob is in bounds.
{
x = e.GetPosition(reference).X; //Get the values and calculate it again.
y = e.GetPosition(reference).Y;
r = Math.Sqrt((x * x) + (y * y));
theta = Math.Atan2(y, x);
x1 = (r * Math.Cos(theta));
y1 = (r * Math.Sin(theta));
x_shape += x1 - x_canvas; //Changing our values and moving the knob.
Canvas.SetLeft(source, x_shape);
x_canvas = x1;
y_shape += y1 - y_canvas;
Canvas.SetTop(source, y_shape);
y_canvas = y1;
XTextBlock.Text = x_shape.ToString();
YTextBlock.Text = y_shape.ToString();
}
}
}
}
}
}
And the XAML just in case:
<Window x:Class="WpfApplication4.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" MouseMove="Window_MouseMove" KeyDown="Window_KeyDown">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<StackPanel Orientation="Vertical">
<TextBlock Text="KNOB POSITION"/>
<TextBlock Name="XTextBlock"/>
<TextBlock Name="YTextBlock"/>
<TextBlock Text="MOUSE POSITION"/>
<TextBlock Name="XMousePos"/>
<TextBlock Name="YMousePos"/>
<TextBlock Text="POLAR COORDINATES"/>
<TextBlock Name="XPolPos"/>
<TextBlock Name="YPolPos"/>
</StackPanel>
<Canvas Name="LayoutRoot" Grid.Column="1">
<Ellipse Fill="#FFF4F4F5" Name ="Joystick" Height="125" Canvas.Left="51" Stroke="Black" Canvas.Top="128" Width="125" MouseLeftButtonDown="Ellipse_MouseLeftButtonDown" MouseLeftButtonUp="Ellipse_MouseLeftButtonUp" MouseMove="Ellipse_MouseMove"/>
<Ellipse Fill="#FFF4F4F5" Name="Knob" Height="15" Canvas.Left="105" Stroke="Black" Canvas.Top="180" Width="15" MouseLeftButtonDown="Ellipse_MouseLeftButtonDown" MouseLeftButtonUp="Ellipse_MouseLeftButtonUp" MouseMove="Ellipse_MouseMove"/>
</Canvas>
</Grid>

When we're talking about joystick the polar coordinate system does not seems to be helpful.
What we need is X and Y offset in range [-1; 1]. We can easily evaluate it knowing Field (big) Radius, Filed Center point and Mouse coordinates.
Here is how it works (remove all events except Ellipse_MouseMove). Member m_vtJoystickPos holds selected Joystick position.
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
UpdateKnobPosition();
}
/// <summary>
/// Current joystick position
/// </summary>
Vector m_vtJoystickPos = new Vector();
private void Ellipse_MouseMove(object sender, MouseEventArgs e)
{
double fJoystickRadius = Joystick.Height * 0.5;
//Make coords related to the center
Vector vtJoystickPos = e.GetPosition(Joystick) -
new Point(fJoystickRadius, fJoystickRadius);
//Normalize coords
vtJoystickPos /= fJoystickRadius;
//Limit R [0; 1]
if (vtJoystickPos.Length > 1.0)
vtJoystickPos.Normalize();
XMousePos.Text = vtJoystickPos.X.ToString();
YMousePos.Text = vtJoystickPos.Y.ToString();
//Polar coord system
double fTheta = Math.Atan2(vtJoystickPos.Y, vtJoystickPos.X);
XPolPos.Text = fTheta.ToString(); //Angle
YPolPos.Text = vtJoystickPos.Length.ToString(); //Radius
if (e.LeftButton == MouseButtonState.Pressed)
{
m_vtJoystickPos = vtJoystickPos;
UpdateKnobPosition();
}
}
void UpdateKnobPosition()
{
double fJoystickRadius = Joystick.Height * 0.5;
double fKnobRadius = Knob.Width * 0.5;
Canvas.SetLeft(Knob, Canvas.GetLeft(Joystick) +
m_vtJoystickPos.X * fJoystickRadius + fJoystickRadius - fKnobRadius);
Canvas.SetTop(Knob, Canvas.GetTop(Joystick) +
m_vtJoystickPos.Y * fJoystickRadius + fJoystickRadius - fKnobRadius);
}
}
I've also included Polar CS evaluation (commented). BTW Polar CS is (R, Angle).
XAML:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApplication1"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<StackPanel Orientation="Vertical">
<TextBlock Text="KNOB POSITION"/>
<TextBlock Name="XTextBlock"/>
<TextBlock Name="YTextBlock"/>
<TextBlock Text="MOUSE POSITION"/>
<TextBlock Name="XMousePos"/>
<TextBlock Name="YMousePos"/>
<TextBlock Text="POLAR COORDINATES"/>
<TextBlock Name="XPolPos"/>
<TextBlock Name="YPolPos"/>
</StackPanel>
<Canvas Name="LayoutRoot" Grid.Column="1">
<Ellipse Fill="#FFF4F4F5" Name ="Joystick" Height="125" Canvas.Left="51" Stroke="Black" Canvas.Top="127" Width="125" MouseMove="Ellipse_MouseMove"/>
<Ellipse Fill="#FFF4F4F5" Name="Knob" Height="16" Canvas.Left="106" Stroke="Black" Canvas.Top="182" Width="15" MouseMove="Ellipse_MouseMove"/>
</Canvas>
</Grid>
</Window>

Related

Zoom on Canvas, centered on mouse position

I know there are several posts on stack and others websites, but it seems I still make something wrong. When I zoom with MouseWheel event, the zoom is always not centered, but the left side of my canvas always stays on the left on my ViewBox, so when I zoom in, I only can see the left of my canvas.
XAML code :
<Grid x:Name="MainGrid">
<Viewbox x:Name="ViewBoxDessin" Stretch="None" HorizontalAlignment="Center" VerticalAlignment="Center">
<Canvas x:Name="monDessin" Background="WhiteSmoke" MouseWheel="monDessin_MouseWheel">
<Canvas.LayoutTransform>
<ScaleTransform x:Name="st" ScaleX="1" ScaleY="-1" CenterX=".5" CenterY=".5" />
</Canvas.LayoutTransform>
</Canvas>
</Viewbox>
<Viewbox x:Name="ViewBoxDessin2" Stretch="None">
<Canvas x:Name="monDessin2">
<Canvas.LayoutTransform>
<ScaleTransform x:Name="st2" ScaleX="1" ScaleY="1" CenterX=".5" CenterY=".5" />
</Canvas.LayoutTransform>
</Canvas>
</Viewbox>
</Grid>
Code behind
public AfficheGraphiquePiece()
{
InitializeComponent();
MakeMyDrawing();
ViewBoxDessin.Width = System.Windows.SystemParameters.PrimaryScreenWidth;
ViewBoxDessin.Height = System.Windows.SystemParameters.PrimaryScreenHeight;
double ech_x = monDessin.Width / System.Windows.SystemParameters.PrimaryScreenWidth;
double ech_y = monDessin.Height / System.Windows.SystemParameters.PrimaryScreenHeight;
double ech = Math.Min(ech_x, ech_y);
this.ech_full = ech;
st.ScaleX = ech;
st.ScaleY = -ech;
st2.ScaleX = ech;
st2.ScaleY = ech;
}
private void monDessin_MouseWheel(object sender, MouseWheelEventArgs e)
{
double zoom = e.Delta > 0 ? 1.1 : 0.9;
if(st.ScaleX<this.ech_full*1.1 && zoom<1)
{
st.ScaleX = this.ech_full;
st.ScaleY = -this.ech_full;
}
else
{
st.ScaleX *= zoom;
st.ScaleY *= zoom;
double coor_x = Mouse.GetPosition(monDessin).X;
double coor_y = Mouse.GetPosition(monDessin).Y;
st.CenterX = coor_x;
st.CenterY = coor_y;
}
}
Excuse me, didn't remove some code, and it could make confusion, just replaced it by a function MakeMyDrawing()
Well, after Clemens advise, and help of that link for matrix use, I could do the following :
XAML :
<Grid x:Name="MainGrid">
<Canvas x:Name="monDessin" Background="WhiteSmoke" MouseWheel="monDessin_MouseWheel" MouseLeftButtonDown="image_MouseLeftButtonDown" MouseMove="image_MouseMove" MouseLeftButtonUp="image_MouseLeftButtonUp" MouseLeave="image_MouseLeave" >
<Canvas.RenderTransform >
<MatrixTransform/>
</Canvas.RenderTransform>
</Canvas>
<Canvas x:Name="monDessin2">
<Canvas.RenderTransform >
<MatrixTransform/>
</Canvas.RenderTransform>
</Canvas>
</Grid>
Code behind
public AfficheGraphiquePiece(Repere rep)
{
InitializeComponent();
ClassGraphique monGraphe = new ClassGraphique(monDessin);
ClassGraphique monGraphe2 = new ClassGraphique(monDessin2);
MakeMyDrawing();
double screenWidth = System.Windows.SystemParameters.PrimaryScreenWidth;
double screenHeight = System.Windows.SystemParameters.PrimaryScreenHeight;
double ech_x = screenWidth/ monDessin.Width ;
double ech_y = screenHeight/ monDessin.Height;
double ech = Math.Min(ech_x, ech_y)*0.9;
this.ech_full = ech;
this.echelleNow = this.ech_full;
MatrixTransform maTrans =(MatrixTransform)monDessin.RenderTransform;
var mat = maTrans.Matrix;
mat.ScaleAt(ech, -ech, 0.1* screenWidth, (screenHeight-monDessin.Height*ech)/2-0.1*screenHeight);
MatrixTransform maTrans2 = (MatrixTransform)monDessin2.RenderTransform;
var mat2 = maTrans2.Matrix;
mat2.ScaleAt(ech, ech, 0.1 * screenWidth, screenHeight*ech-((screenHeight - monDessin.Height * ech) / 2 - 0.1 * screenHeight));
maTrans.Matrix = mat;
maTrans2.Matrix = mat2;
}
private void monDessin_MouseWheel(object sender, MouseWheelEventArgs e)
{
try
{
var position = e.GetPosition(monDessin);
MatrixTransform transform = (MatrixTransform)monDessin.RenderTransform;
MatrixTransform transform2 = (MatrixTransform)monDessin2.RenderTransform;
var matrix = transform.Matrix;
var matrix2 = transform2.Matrix;
var scale = e.Delta >= 0 ? 1.1 : (1.0 / 1.1);
this.echelleNow *= scale;
matrix.ScaleAtPrepend(scale, scale, position.X, position.Y);
matrix2.ScaleAtPrepend(scale, scale, position.X,monDessin.Height-position.Y);
monDessin.RenderTransform = new MatrixTransform(matrix);
monDessin2.RenderTransform = new MatrixTransform(matrix2);
}
catch { }
}
Here is a very basic example for zooming and panning a Canvas with fixed initial size. The MatrixTransform in the RenderTransform of the inner Canvas provides the necessary transformations, while the outer Canvas handles mouse input and sets an initial scaling.
<Canvas Background="Transparent"
SizeChanged="ViewportSizeChanged"
MouseLeftButtonDown="ViewportMouseLeftButtonDown"
MouseLeftButtonUp="ViewportMouseLeftButtonUp"
MouseMove="ViewportMouseMove"
MouseWheel="ViewportMouseWheel">
<Canvas x:Name="canvas" Width="1000" Height="600">
<Canvas.RenderTransform>
<MatrixTransform x:Name="transform"/>
</Canvas.RenderTransform>
<Ellipse Fill="Red" Width="100" Height="100" Canvas.Left="100" Canvas.Top="100"/>
<Ellipse Fill="Green" Width="100" Height="100" Canvas.Right="100" Canvas.Bottom="100"/>
</Canvas>
</Canvas>
Code behind:
private Point? mousePos;
private void ViewportSizeChanged(object sender, SizeChangedEventArgs e)
{
((MatrixTransform)canvas.RenderTransform).Matrix = new Matrix(
e.NewSize.Width / canvas.ActualWidth,
0, 0,
e.NewSize.Height / canvas.ActualHeight,
0, 0);
}
private void ViewportMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
var viewport = (UIElement)sender;
viewport.CaptureMouse();
mousePos = e.GetPosition(viewport);
}
private void ViewportMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
((UIElement)sender).ReleaseMouseCapture();
mousePos = null;
}
private void ViewportMouseMove(object sender, MouseEventArgs e)
{
if (mousePos.HasValue)
{
var pos = e.GetPosition((UIElement)sender);
var matrix = transform.Matrix;
matrix.Translate(pos.X - mousePos.Value.X, pos.Y - mousePos.Value.Y);
transform.Matrix = matrix;
mousePos = pos;
}
}
private void ViewportMouseWheel(object sender, MouseWheelEventArgs e)
{
var pos = e.GetPosition((UIElement)sender);
var matrix = transform.Matrix;
var scale = e.Delta > 0 ? 1.1 : 1 / 1.1;
matrix.ScaleAt(scale, scale, pos.X, pos.Y);
transform.Matrix = matrix;
}

Displaying grid lines around individual pixels when zooming

I'm experimenting with the concept of drawing grid lines over a control and was wondering what adjustments I might need to make to make this actually work. I found some code on another post that enables grid lines to be drawn OnRender over a canvas. Here's what that looks like:
public class MyCanvas : Canvas
{
public bool IsGridVisible = true;
protected override void OnRender(System.Windows.Media.DrawingContext dc)
{
base.OnRender(dc);
if (IsGridVisible)
{
// Draw GridLines
Pen pen = new Pen(Brushes.Black, 1);
pen.DashStyle = DashStyles.Solid;
for (double x = 0; x < this.ActualWidth; x += 2)
{
dc.DrawLine(pen, new Point(x, 0), new Point(x, this.ActualHeight));
}
for (double y = 0; y < this.ActualHeight; y += 2)
{
dc.DrawLine(pen, new Point(0, y), new Point(this.ActualWidth, y));
}
}
}
public MyCanvas()
{
DefaultStyleKey = typeof(MyCanvas);
}
}
This part: y += 2 indicates how many other pixels/points to wait before drawing next line, though I am uncertain of it's correctness.
Here's the xaml:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="30"/>
</Grid.RowDefinitions>
<ScrollViewer>
<local:MyCanvas>
<local:MyCanvas.LayoutTransform>
<ScaleTransform ScaleX="{Binding ElementName=Slider, Path=Value}" ScaleY="{Binding ElementName=Slider, Path=Value}"/>
</local:MyCanvas.LayoutTransform>
<Image Canvas.Top="2" Canvas.Left="2" Source="C:\Users\Me\Pictures\nyan-wallpaper2.jpg" Width="325" RenderOptions.BitmapScalingMode="NearestNeighbor"/>
</local:MyCanvas>
</ScrollViewer>
<Slider x:Name="Slider" Maximum="500" Grid.Row="1" Value="1"/>
</Grid>
Here are screenshots of what the above results in.
As you can see, the grid lines change in size as you zoom and the lines themselves do not snap around each individual pixel. I highlighted an example pixel in red to show how small the lines should be versus how they actually are.
I read that the thickness of the pen should be divided by the scale value, however, I tested this by replacing Pen pen = new Pen(Brushes.Black, 1); with Pen pen = new Pen(Brushes.Black, 1 / 3); and set the ScaleX and ScaleY of MyCanvas to 3. At that point, no lines showed at all.
Any help at all is immensely valued!
Got it working like this for anyone curious:
MainWindow.xaml.cs
namespace Test
{
public class MyCanvas : Canvas
{
public bool IsGridVisible = false;
#region Dependency Properties
public static DependencyProperty ZoomValueProperty = DependencyProperty.Register("ZoomValue", typeof(double), typeof(MyCanvas), new FrameworkPropertyMetadata(1d, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnZoomValueChanged));
public double ZoomValue
{
get
{
return (double)GetValue(ZoomValueProperty);
}
set
{
SetValue(ZoomValueProperty, value);
}
}
private static void OnZoomValueChanged(DependencyObject Object, DependencyPropertyChangedEventArgs e)
{
}
#endregion
protected override void OnRender(System.Windows.Media.DrawingContext dc)
{
base.OnRender(dc);
IsGridVisible = ZoomValue > 4.75 ? true : false;
if (IsGridVisible)
{
// Draw GridLines
Pen pen = new Pen(Brushes.Black, 1 / ZoomValue);
pen.DashStyle = DashStyles.Solid;
for (double x = 0; x < this.ActualWidth; x += 1)
{
dc.DrawLine(pen, new Point(x, 0), new Point(x, this.ActualHeight));
}
for (double y = 0; y < this.ActualHeight; y += 1)
{
dc.DrawLine(pen, new Point(0, y), new Point(this.ActualWidth, y));
}
}
}
public MyCanvas()
{
DefaultStyleKey = typeof(MyCanvas);
}
}
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private WriteableBitmap bitmap = new WriteableBitmap(500, 500, 96d, 96d, PixelFormats.Bgr24, null);
private void Button_Click(object sender, RoutedEventArgs e)
{
int size = 1;
Random rnd = new Random(DateTime.Now.Millisecond);
bitmap.Lock(); // Lock() and Unlock() could be moved to the DrawRectangle() method. Just do some performance tests.
for (int y = 0; y < 500; y++)
{
for (int x = 0; x < 500; x++)
{
byte colR = (byte)rnd.Next(256);
byte colG = (byte)rnd.Next(256);
byte colB = (byte)rnd.Next(256);
DrawRectangle(bitmap, size * x, size * y, size, size, Color.FromRgb(colR, colG, colB));
}
}
bitmap.Unlock(); // Lock() and Unlock() could be moved to the DrawRectangle() method. Just do some performance tests.
Image.Source = bitmap; // This should be done only once
}
public void DrawRectangle(WriteableBitmap writeableBitmap, int left, int top, int width, int height, Color color)
{
// Compute the pixel's color
int colorData = color.R << 16; // R
colorData |= color.G << 8; // G
colorData |= color.B << 0; // B
int bpp = writeableBitmap.Format.BitsPerPixel / 8;
unsafe
{
for (int y = 0; y < height; y++)
{
// Get a pointer to the back buffer
int pBackBuffer = (int)writeableBitmap.BackBuffer;
// Find the address of the pixel to draw
pBackBuffer += (top + y) * writeableBitmap.BackBufferStride;
pBackBuffer += left * bpp;
for (int x = 0; x < width; x++)
{
// Assign the color data to the pixel
*((int*)pBackBuffer) = colorData;
// Increment the address of the pixel to draw
pBackBuffer += bpp;
}
}
}
writeableBitmap.AddDirtyRect(new Int32Rect(left, top, width, height));
}
}
}
MainWindow.xaml
<Window x:Class="Test.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Test"
mc:Ignorable="d"
Title="MainWindow"
Height="Auto"
Width="Auto"
WindowStartupLocation="CenterScreen"
WindowState="Maximized">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="30"/>
</Grid.RowDefinitions>
<ScrollViewer>
<local:MyCanvas ZoomValue="{Binding ElementName=ScaleTransform, Path=ScaleX}">
<local:MyCanvas.LayoutTransform>
<ScaleTransform x:Name="ScaleTransform" ScaleX="{Binding ElementName=Slider, Path=Value}" ScaleY="{Binding ElementName=Slider, Path=Value}"/>
</local:MyCanvas.LayoutTransform>
<Image Canvas.Top="1" Canvas.Left="1" x:Name="Image" RenderOptions.BitmapScalingMode="NearestNeighbor"/>
</local:MyCanvas>
</ScrollViewer>
<StackPanel Grid.Row="1" Orientation="Horizontal">
<Slider x:Name="Slider" Maximum="100" Minimum="0.5" Value="1" Width="200"/>
<Button Click="Button_Click" Content="Click Me!"/>
</StackPanel>
</Grid>
</Window>
We generate a bitmap with random colored pixels and then render the grid lines only if zoomed up close. Performance-wise, this is actually better than expected. I should note, though, that if you attempt to zoom below 50%, the app crashes. Not sure if it's an issue with the grid lines being drawn at a minute size (IsGridVisible = true where ZoomValue < 0.5) or with the bitmap being generated. Either way, cheers!
Update
Didn't realize the grid lines are still behind the contents of the canvas. Haven't worked out a solution for that yet...
Update 2
Replace:
<local:MyCanvas ZoomValue="{Binding ElementName=ScaleTransform, Path=ScaleX}">
<local:MyCanvas.LayoutTransform>
<ScaleTransform x:Name="ScaleTransform" ScaleX="{Binding ElementName=Slider, Path=Value}" ScaleY="{Binding ElementName=Slider, Path=Value}"/>
</local:MyCanvas.LayoutTransform>
<Image Canvas.Top="1" Canvas.Left="1" x:Name="Image" RenderOptions.BitmapScalingMode="NearestNeighbor"/>
</local:MyCanvas>
With:
<Grid>
<Canvas>
<Canvas.LayoutTransform>
<ScaleTransform ScaleX="{Binding ElementName=Slider, Path=Value}" ScaleY="{Binding ElementName=Slider, Path=Value}"/>
</Canvas.LayoutTransform>
<Image Canvas.Top="5" Canvas.Left="5" x:Name="Image" RenderOptions.BitmapScalingMode="NearestNeighbor"/>
</Canvas>
<local:MyGrid ZoomValue="{Binding ElementName=ScaleTransform, Path=ScaleX}">
<local:MyGrid.LayoutTransform>
<ScaleTransform x:Name="ScaleTransform" ScaleX="{Binding ElementName=Slider, Path=Value}" ScaleY="{Binding ElementName=Slider, Path=Value}"/>
</local:MyGrid.LayoutTransform>
</local:MyGrid>
</Grid>
I believe another boost in performance as we are utilizing a simpler control to display the grid lines, plus, the grid lines can be placed either below or above desired controls.
Update 3
I have decided to post my latest solution, which is significantly more efficient and can all be done in XAML:
<Grid>
<Grid.Background>
<DrawingBrush Viewport="0,0,5,5" ViewportUnits="Absolute" TileMode="Tile">
<DrawingBrush.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Geometry="M-.5,0 L50,0 M0,10 L50,10 M0,20 L50,20 M0,30 L50,30 M0,40 L50,40 M0,0 L0,50 M10,0 L10,50 M20,0 L20,50 M30,0 L30,50 M40,0 L40,50">
<GeometryDrawing.Pen>
<Pen Thickness="1" Brush="Black" />
</GeometryDrawing.Pen>
</GeometryDrawing>
</DrawingGroup.Children>
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
</Grid.Background>
</Grid>

Scale, translate and crop an image in windows phone 8.1 silverlight

I need the functionality of an image scale, translate and cropping just like what a photo chooser task do in windows phone 8.1. I can not use photo chooser task because I will not select the photo from media photo library or capture the image. For my purpose the image will be selected from a server saved picture list. I have written some code for this but it's not working as I desire. I am working on it for 4 weeks but hardly find the perfect solution. Here is my code.
This is the xaml code
<phone:PhoneApplicationPage
x:Class="TransformationLearning.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
shell:SystemTray.IsVisible="True">
<!--LayoutRoot is the root grid where all page content is placed-->
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
<TextBlock Text="Transformation" Style="{StaticResource PhoneTextNormalStyle}" Margin="12,0"/>
<TextBlock Text="page name" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
</StackPanel>
<Grid Grid.Row="1" Background="Yellow">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid Name="grid"
Grid.Row="0"
Height="400"
Width="400"
>
<Image x:Name="myImage" Source="/Assets/Lion.jpg"
Stretch="UniformToFill"
ManipulationCompleted="CompleteManipulation"
ManipulationDelta="DeltaManipulation"
Loaded="ImageLoaded"
Grid.Row="1"
>
<Image.RenderTransform>
<CompositeTransform x:Name="myComposite"
/>
</Image.RenderTransform>
</Image>
<Rectangle Width="200"
Height="200"
Name="myRectangle"
Margin="100 100"
Stroke="White"
StrokeThickness="3" Grid.Row="1">
</Rectangle>
</Grid>
<Image Name="myCropPicture" Grid.Row="1"/>
</Grid>
</Grid>
</phone:PhoneApplicationPage>
and this is the code behind:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Navigation;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;
using TransformationLearning.Resources;
using Microsoft.Xna.Framework;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.IO;
using Lumia.Imaging;
using Lumia.Imaging.Transforms;
using System.Threading.Tasks;
using Windows.Storage.Streams;
using Windows.Storage;
using System.Windows.Controls.Primitives;
using System.Runtime.InteropServices.WindowsRuntime;
namespace TransformationLearning
{
public partial class MainPage : PhoneApplicationPage
{
WriteableBitmap WB_CapturedImage;//for original image
WriteableBitmap WB_CroppedImage;//for cropped image
double width, height;
WriteableBitmap wb;
CompositeTransform transform;
GeneralTransform gt, gt1;
System.Windows.Point absolutePosition, absolutePosition1;
public MainPage()
{
InitializeComponent();
transform = new CompositeTransform();
}
private void ImageLoaded(object sender, RoutedEventArgs e)
{
wb = new WriteableBitmap(myImage, null);
myComposite.CenterX = 150;
myComposite.CenterY = 150;
}
private double BoundaryCheck(double value)
{
if (value < 1.0)
return 1.0;
else if (value > 2.0)
return 2.0;
else
return value;
}
private void DeltaManipulation(object sender, System.Windows.Input.ManipulationDeltaEventArgs e)
{
transform = myComposite;
if (e.DeltaManipulation.Scale.X == 0.0 || e.DeltaManipulation.Scale.Y == 0.0)
{
myComposite.ScaleX *= 1.0;
myComposite.ScaleY *= 1.0;
}
else if (e.DeltaManipulation.Scale.X > 0.0)
{
double value = BoundaryCheck(myComposite.ScaleX*e.DeltaManipulation.Scale.X);
myComposite.ScaleX = myComposite.ScaleY = value;
gt = myImage.TransformToVisual(grid);
absolutePosition = gt.Transform(new System.Windows.Point(0, 0));
gt1 = myRectangle.TransformToVisual(grid);
absolutePosition1 = gt1.Transform(new System.Windows.Point(0, 0));
if ((absolutePosition1.X <= absolutePosition.X)
|| (absolutePosition1.Y <= absolutePosition.Y)
|| (absolutePosition1.X + myRectangle.Width >= absolutePosition.X + wb.PixelWidth)
|| (absolutePosition1.Y + myRectangle.Height >= absolutePosition.Y + wb.PixelHeight))
{
myComposite = transform;
}
}
gt = myImage.TransformToVisual(grid);
absolutePosition = gt.Transform(new System.Windows.Point(0, 0));
gt1 = myRectangle.TransformToVisual(grid);
absolutePosition1 = gt1.Transform(new System.Windows.Point(0, 0));
if (e.DeltaManipulation.Translation.Y > 0.0 && (absolutePosition1.Y >= (absolutePosition.Y + e.DeltaManipulation.Translation.Y)))
{
myComposite.TranslateY += (e.DeltaManipulation.Translation.Y-2);
}
if (e.DeltaManipulation.Translation.X > 0.0 && (absolutePosition1.X >= (absolutePosition.X + e.DeltaManipulation.Translation.X)))
{
myComposite.TranslateX += (e.DeltaManipulation.Translation.X - 2);
}
if (e.DeltaManipulation.Translation.Y < 0.0 && ((absolutePosition1.Y + myRectangle.Height) <= (absolutePosition.Y + e.DeltaManipulation.Translation.Y + wb.PixelHeight)))
{
myComposite.TranslateY += (e.DeltaManipulation.Translation.Y + 2);
}
if (e.DeltaManipulation.Translation.X < 0.0 && ((absolutePosition1.X + myRectangle.Width) <= (absolutePosition.X + e.DeltaManipulation.Translation.X + wb.PixelWidth)))
{
myComposite.TranslateX += (e.DeltaManipulation.Translation.X + 2);
}
wb = new WriteableBitmap(myImage, myComposite);
gt = myImage.TransformToVisual(grid);
absolutePosition = gt.Transform(new System.Windows.Point(0, 0));
}
private void CompleteManipulation(object sender, System.Windows.Input.ManipulationCompletedEventArgs e)
{
App.RootFrame.RenderTransform = new CompositeTransform();
grid.UpdateLayout();
CropImage();
App.RootFrame.RenderTransform = new CompositeTransform();
grid.UpdateLayout();
}
private async void CropImage()
{
var ms = new MemoryStream();
wb.SaveJpeg(ms, wb.PixelWidth, wb.PixelHeight, 0, 100);
ms.Position = 0;
gt1 = myRectangle.TransformToVisual(myImage);
absolutePosition1 = gt1.Transform(new System.Windows.Point(0, 0));
using (var source = new StreamImageSource(ms))
{
using (var filterEffect = new FilterEffect(source))
{
var filter = new CropFilter(new Windows.Foundation.Rect(absolutePosition1.X, absolutePosition1.Y, myRectangle.Width, myRectangle.Height));
filterEffect.Filters = new IFilter[] { filter };
var target = new WriteableBitmap((int)(myRectangle.Width), (int)(myRectangle.Height));
using (var renderer = new WriteableBitmapRenderer(filterEffect, target))
{
try
{
await renderer.RenderAsync();
}
catch (Exception t)
{
System.Diagnostics.Debug.WriteLine(t.InnerException);
System.Diagnostics.Debug.WriteLine(t.Message);
}
}
myCropPicture.Source = target;
}
}
}
}
}
Here I have used Lumia Imaging SDK crop filter for cropping the image. "myImage" is the source image and "myCropPicture" is used for showing the cropped image. For transformation I have used composite transform. When I scale the source image this code can not crop that image accordingly.I need the help badly.
did you check this post. it might come in handy
Image crop control for Silverlight or Windows Phone

WPF - Plotting a cosine function when radiobutton is clicked

I just started learning C# and I want to plot a cosine when user presses the radiobutton using WPF GUI interface. I think I am having trouble how to use call objects within different class. Thanks in advance and below is my code:
namespace WpfApplication2
{
using OxyPlot;
using OxyPlot.Annotations;
using OxyPlot.Axes;
using OxyPlot.Series;
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void button_Click_2(object sender, RoutedEventArgs e)
{
if (radioButton1.IsChecked == true)
{
MessageBox.Show("Plot Cosine");
//I think solution should be something like this
//MainViewModel.MyModel.Series.Add(new FunctionSeries(Math.Cos, -10, 10, 0.01, "cos(x)"));
}
}
}
public class MainViewModel : Window
{
//Plotting without any user input
public const double Pi = 3.14159265358979323846;
public const int SpeedOfLight = 3 * 10 ^ 8; // m per sec.
//OxyPlot.Wpf.PlotModel plot = new OxyPlot.Wpf.PlotView();
public MainViewModel()
{
MyModel = new PlotModel { Title = "Your Equation", LegendTitle = "Equations" };
MyModel.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Title = "Distance" });
MyModel.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Title = "Height" });
//Determine your range for the plot
//MyModel.Axes.Add(new LinearAxis(AxisPosition.Bottom, -10, 10));
//MyModel.Axes.Add(new LinearAxis(AxisPosition.Left, -5, 5));
MyModel.Series.Add(new FunctionSeries(Math.Cos, -10, 10, 0.01, "cos(x)"));
MyModel.Series.Add(new FunctionSeries(Math.Sin, -10, 10, 0.01, "sin(x)"));
LineSeries linePoints = new LineSeries() { };
double x, y;
DataPoint XYpoint = new DataPoint();
for (x = -10; x <= 10; x += 0.01)
{
//Make sure not 1/3 since C# will read it as integer divided by integer hence 1/3=0
//Use Math.Pow for powers
//Definately Matlab is easier to plot stuff XD
y = 1.0 / 2.0 * Math.Pow(x, 2) + 1;
XYpoint = new DataPoint(x, y);
linePoints.Points.Add(XYpoint);
}
MyModel.Series.Add(linePoints);
MyModel.InvalidatePlot(true);
}
public PlotModel MyModel { get; private set; }
}
}
Below is XAML code:
<Window x:Name="plot" x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:oxy="http://oxyplot.org/wpf"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApplication2"
mc:Ignorable="d"
Title="Plots" Height="450.307" Width="955.532" Background="White">
<Window.DataContext>
<local:MainViewModel/>
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="73*"/>
<RowDefinition Height="11*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="145*"/>
<ColumnDefinition Width="329*"/>
</Grid.ColumnDefinitions>
<oxy:PlotView Title="{Binding Title}" Margin="4,0,0,0" Model="{Binding MyModel}" Grid.Column="1" >
<oxy:PlotView.Series>
<oxy:LineSeries ItemsSource="{Binding Points}"/>
</oxy:PlotView.Series>
</oxy:PlotView>
<Label x:Name="label" HorizontalAlignment="Left" Height="30" Margin="120,185,0,0" VerticalAlignment="Top" Width="142"/>
<RadioButton x:Name="radioButton1" Content="Plot Cosine" Grid.Column="1" HorizontalAlignment="Left" Height="20" Margin="50,10,0,0" Grid.Row="1" VerticalAlignment="Top" Width="85" />
<Button x:Name="button1" Content="Clear" HorizontalAlignment="Left" Height="35" Margin="120,7,0,0" Grid.Row="1" VerticalAlignment="Top" Width="142" Click="button_Click_2"/>
</Grid>
</Window>
C#
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;
using OxyPlot;
using OxyPlot.Series;
using OxyPlot.Axes;
namespace WpfApplication2
{
/// <summary>
/// Interaktionslogik für MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public PlotModel MyModel { get; private set; }
public MainWindow()
{
InitializeComponent();
MyModel = new PlotModel { Title = "Your Equation", LegendTitle = "Equations" };
MyModel.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Title = "Distance" });
MyModel.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Title = "Height" });
}
private void button1_Click(object sender, RoutedEventArgs e)
{
if (radioButton1.IsChecked == true)
{
//MessageBox.Show("Plot Cosine");
graph();
}
}
public double getValue(int x, int y)
{
return (10 * x * x + 11 * x * y * y + 12 * x * y);
}
//setting the values to the function
public FunctionSeries GetFunction()
{
int n = 100;
FunctionSeries serie = new FunctionSeries();
for (int x = 0; x < n; x++)
{
for (int y = 0; y < n; y++)
{
//adding the points based x,y
DataPoint data = new DataPoint(x, getValue(x, y));
//adding the point to the serie
serie.Points.Add(data);
}
}
//returning the serie
return serie;
}
public void graph()
{
MyModel = new PlotModel { Title = "example" };
MyModel.LegendPosition = LegendPosition.RightBottom;
MyModel.LegendPlacement = LegendPlacement.Outside;
MyModel.LegendOrientation = LegendOrientation.Horizontal;
MyModel.Series.Add(GetFunction());
var Yaxis = new OxyPlot.Axes.LinearAxis();
OxyPlot.Axes.LinearAxis XAxis = new OxyPlot.Axes.LinearAxis { Position = OxyPlot.Axes.AxisPosition.Bottom, Minimum = 0, Maximum = 100 };
XAxis.Title = "X";
Yaxis.Title = "10 * x * x + 11 * x*y*y + 12*x*y";
MyModel.Axes.Add(Yaxis);
MyModel.Axes.Add(XAxis);
this.plot.Model = MyModel;
}
}
}
XAML
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:oxy="http://oxyplot.org/wpf"
xmlns:local="clr-namespace:WpfApplication2"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="73*"/>
<RowDefinition Height="11*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="145*"/>
<ColumnDefinition Width="329*"/>
</Grid.ColumnDefinitions>
<oxy:PlotView Margin="4,0,0,0" Grid.Column="1" Name="plot" >
<!--<oxy:PlotView.Series>
<oxy:LineSeries ItemsSource="{Binding Points}"/>
</oxy:PlotView.Series>-->
</oxy:PlotView>
<Label x:Name="label" HorizontalAlignment="Left" Height="30" Margin="120,185,0,0" VerticalAlignment="Top" Width="142"/>
<RadioButton x:Name="radioButton1" IsChecked="True" Content="Plot Cosine" Grid.Column="1" HorizontalAlignment="Left" Height="20" Margin="50,10,0,0" Grid.Row="1" VerticalAlignment="Top" Width="85" />
<Button x:Name="button1" Content="Clear" HorizontalAlignment="Left" Height="35" Margin="120,7,0,0" Grid.Row="1" VerticalAlignment="Top" Width="142" Click="button1_Click"/>
</Grid>
</Window>

Using a rectangle to create a search region on an image

I have an Image viewer that displays an image. I want to draw a rectangle using the mouse over the image and get the x and y coordinates of the rectangle (X1, X2, Y1, and Y2). I will use these coordinates to create a search region and find the max and min values in an array that have the exact number of pixels as the image in both axes.
Can anyone guide me to a direction to start please?
You should use a canvas to display the image and draw a rectangle over it.
Example:
MainWindow.xaml:
<Window x:Class="CanvasRectangleSample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow">
<Grid>
<Canvas x:Name="SampleImageCanvas"
MouseMove="SampleImageCanvas_MouseMove"
MouseDown="SampleImageCanvas_MouseDown"
Width="512" Height="389">
<Canvas.Background>
<!--Here you set the image to display -> You probably want to bind it to something. -->
<ImageBrush x:Name="SampleImage" Stretch="Uniform" ImageSource="C:\Users\Public\Pictures\Sample Pictures\Koala.jpg">
</ImageBrush>
</Canvas.Background>
<!-- Here you draw whatever you want on the canvas. -->
<!-- You'll probably want to bind its width and height to something too. -->
<Rectangle x:Name="ROI" Stroke="#FFF1133E" Width="50" Height="50"/>
</Canvas>
</Grid>
</Window>
MainWindow.xaml.cs:
using System.Windows;
using System.Windows.Input;
using System.Windows.Controls;
namespace CanvasRectangleSample
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
this.DataContext = this;
InitializeComponent();
}
// Handling the redrawing of the rectangle according to mouse location
private void SampleImageCanvas_MouseMove(object sender, MouseEventArgs e)
{
//get mouse location relative to the canvas
Point pt = e.MouseDevice.GetPosition(sender as Canvas);
//here you set the rectangle loction relative to the canvas
Canvas.SetLeft(ROI, pt.X - (int)(ROI.Width / 2));
Canvas.SetTop(ROI, pt.Y - (int)(ROI.Height / 2));
}
private void SampleImageCanvas_MouseDown(object sender, MouseButtonEventArgs e)
{
//Here you should handle saving the rectangle location
//don't forget to calculate the proportion between Canvas's size and real Image's size.
}
}
}
If you want you can limit the rectangle relocation to the canvas area with an if expression checking if the canvas area containes the mouse locaion
Thanks for the pointers and help:
Here is my finished code and it works. You place the mouse anywhere on the canvas hold mouse down and drag to create the rectangle. It could use some more improvement to drag and create the rectangle in any direction.
XAML:
<Canvas Name="ImageCanvas"
MouseMove="ImageCanvas_MouseMove"
MouseDown="ImageCanvas_MouseDown"
Height="240" Width="320"
HorizontalAlignment="Left"
Margin="87,514,0,0" VerticalAlignment="Top" MouseLeftButtonUp="ImageCanvas_MouseLeftButtonUp">
<Canvas.Background>
<ImageBrush x:Name="Image" Stretch="Uniform" ImageSource="C:\image.bmp">
</ImageBrush>
</Canvas.Background>
<Rectangle x:Name="ROI" Stroke="#FFF1133E" Width="20" Height="20" Canvas.Left="155" Canvas.Top="115" />
</Canvas>
Code:
double topLeftX = 0;
double topLeftY = 0;
double bottomRightX = 0;
double bottomrigthY = 0;
bool setRect = false;
private void ImageCanvas_MouseDown(object sender, MouseButtonEventArgs e)
{
topLeftY = topLeftX = bottomrigthY = bottomRightX = 0;
setRect = true;
System.Windows.Point pt = e.MouseDevice.GetPosition(sender as Canvas);
topLeftX = pt.X; topLeftY = pt.Y;
}
private void ImageCanvas_MouseMove(object sender, System.Windows.Input.MouseEventArgs e)
{
if (setRect == true)
{
//get mouse location relative to the canvas
System.Windows.Point pt = e.MouseDevice.GetPosition(sender as Canvas);
Canvas.SetLeft(ROI, topLeftX);
Canvas.SetTop(ROI, topLeftY);
ROI.Width = System.Math.Abs((int)(pt.X - topLeftX));
ROI.Height = System.Math.Abs((int)(pt.Y - topLeftY));
commandReturnTB.Text = (Convert.ToString(pt.X) + "," + Convert.ToString(pt.Y))+"\n";
}
}
private void ImageCanvas_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
System.Windows.Point pt = e.MouseDevice.GetPosition(sender as Canvas);
bottomRightX = pt.X;
bottomrigthY = pt.Y;
ROI.Width = System.Math.Abs((int)(bottomRightX - topLeftX));
ROI.Height = System.Math.Abs((int)(bottomrigthY - topLeftY));
Canvas.SetLeft(ROI, topLeftX);
Canvas.SetTop(ROI, topLeftY);
setRect = false;
commandReturnTB.Text = topLeftX + "," + topLeftY + "--" + bottomRightX + "," + bottomrigthY;
}

Categories

Resources