Get coordinates on mouse click in viewport wpf - c#

I want to return the coordinates of mouse click in viewport. When I click inside the viewport, it is showing me screen coordinates (for example 480,650) but the range for my viewport is say (0,0) to say (10,10). How do I get the coordinates of mouse clicked to viewport coordinate system.
My xaml code:
<Window x:Class="PathOptimization.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Geometries"
xmlns:tool3d="clr-namespace:_3DTools;assembly=3DTools"
Title="MainWindow" Height="350" Width="1000" WindowState="Maximized">
<Grid x:Name="grdView" >
<Grid.ColumnDefinitions >
<ColumnDefinition Width="200"></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition Width="200"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Viewport3D x:Name="viewport" Grid.Column="1" MouseDown="button_MouseDown" Margin="0,35,0,0">
<ContainerUIElement3D>
<ModelUIElement3D >
..............................
</ModelUIElement3D>
</ContainerUIElement3D>
<Viewport3D.Camera>
<PerspectiveCamera Position="0,1,9"
LookDirection="0,0,-2"
UpDirection="0,1,0"/>
</Viewport3D.Camera>
</Viewport3D>
When I click on the screen inside the viewport, I am getting screen coordinates. How can I get coordinates relative to viewport.
private void button_MouseDown(object sender, MouseButtonEventArgs e)
{
Point pointToWindow = e.GetPosition(viewport);
Point pointToScreen = PointToScreen(pointToWindow);
double x = (pointToScreen.X);
double y = (pointToScreen.Y);
MessageBox.Show(x + "," + y);
}

Related

How to create sharp 1px lines un UWP? [duplicate]

I overriden Border control and in my overriden OnRender I do:
protected override void OnRender(System.Windows.Media.DrawingContext dc)
{
this.SnapsToDevicePixels = true;
this.VisualEdgeMode = EdgeMode.Aliased;
var myPen = new Pen(new SolidColorBrush(Colors.LightGray), 1);
dc.DrawLine(myPen, new Point(1, 1), new Point(1, RenderSize.Height - 1));
return;
Which give me this result:
Question:
Is anybody can tell me why my code draw a line that start at (0,1) while it is suppose to start at (1, 1) like written in code ?
My DPI are 96, 96.
For ref, this is my xaml:
<Window xmlns:MyControls="clr-namespace:MyControls;assembly=MyControls" x:Class="TestControls.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">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<MyControls:Border3D BorderThickness="3" BorderBrush="Aqua">
<Rectangle Width="74" Height="3" HorizontalAlignment="Left">
<Rectangle.Fill>
<SolidColorBrush Color="#40FF0000">
</SolidColorBrush>
</Rectangle.Fill>
</Rectangle>
</MyControls:Border3D>
<Rectangle Grid.Row="1" Grid.Column="0" Width="80" Grid.ColumnSpan="2" HorizontalAlignment="Left">
<Rectangle.Fill>
<SolidColorBrush Color="LightGray"></SolidColorBrush>
</Rectangle.Fill>
</Rectangle>
</Grid>
</Window>
Keep in mind that (0, 0) is not the center of the upper left pixel. Instead it is the upper left corner of that pixel. In order to draw a line with a stroke thickness of 1 in the middle of the second pixel column (with index 1) you would have to draw from (1.5, 1) to (1.5, RenderSize.Height - 1):
dc.DrawLine(myPen, new Point(1.5, 1), new Point(1.5, RenderSize.Height - 1));
Setting SnapsToDevicePixels = true made your line snap to the left by half a pixel.
If you would use PenLineCap.Square for both the StartLineCap and EndLineCap properties of the line's Pen, you could draw from exactly one pixel center to the other:
var myPen = new Pen(Brushes.LightGray, 1);
myPen.StartLineCap = PenLineCap.Square;
myPen.EndLineCap = PenLineCap.Square;
dc.DrawLine(myPen, new Point(1.5, 1.5), new Point(1.5, RenderSize.Height - 1.5));

Click and drag box over image - WPF

I'm having trouble getting the selectionBox not to center. In the image below, I tried to draw a box around the word Final Bill (accented ms paint box) by clicking and dragging but the resulting selectionBox (dashed line in red) that outputs always starts in the center. The rectangle values that are calculated and saved in the mouseUp event are all correct, suggesting perhaps a XAML display issue?
I am very knew to WPF/XAML and front end stuff in general.
EDIT: By placing just the selectionBox in a <Canvas> tag I was able to get it almost working. It no longer centers but the start point appears to be twice as far from the left and top borders as the mouse is when clicked.
XAML
<DockPanel Width="Auto" Margin="225,65,5,5">
<Border x:Name="img_Border" ClipToBounds="True" Height="Auto" Width="Auto" Margin="0,0,0,0" VerticalAlignment="Top">
<Grid>
<ScrollViewer>
<Image x:Name="img_Box" ClipToBounds="True" MouseMove="img_Box_MouseMove" MouseWheel="img_Box_MouseWheel">
</Image>
</ScrollViewer>
<Rectangle x:Name="selectionBox" Visibility="Collapsed" Stroke="Red" StrokeThickness="3" StrokeDashArray="3,1">
</Rectangle>
</Grid>
</Border>
</DockPanel>
I can get the selectionBox to work correctly if I use <Canvas> tags. but that causes the image to not fit inside the <DockPanel>.
c#
public MainWindow()
{
InitializeComponent();
img_Box.MouseLeftButtonDown += img_Box_MouseLeftButtonDown;
img_Box.MouseLeftButtonUp += img_Box_MouseLeftButtonUp;
img_Box.MouseMove += img_Box_MouseMove;
Point mouseDownPos;
}
private void img_Box_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
img_Box.CaptureMouse();
var tt = (TranslateTransform)((TransformGroup)img_Box.RenderTransform).Children.First(tr => tr is TranslateTransform);
double BoxX = (e.GetPosition(img_Box).X / img_Box.ActualWidth) * 1;
double BoxY = (e.GetPosition(img_Box).Y / img_Box.ActualHeight) * 1;
double xValue = Math.Round((BoxX * img_Box.Source.Width), 0);
double yValue = Math.Round((BoxY * img_Box.Source.Height), 0);
StartDrag = new System.Windows.Point(xValue, yValue);
mouseDownPos.X = (int)xValue;
mouseDownPos.Y = (int)yValue;
Canvas.SetLeft(selectionBox, xValue);
Canvas.SetTop(selectionBox, yValue);
selectionBox.Width = 0;
selectionBox.Height = 0;
selectionBox.Visibility = Visibility.Visible;
}
private void img_Box_MouseMove(object sender, MouseEventArgs e)
{
double x = e.GetPosition(img_Box).X;
double y = e.GetPosition(img_Box).Y;
double BoxX = (x / img_Box.ActualWidth) * 1;
double BoxY = (y / img_Box.ActualHeight) * 1;
double xValue = Math.Round((BoxX * img_Box.Source.Width), 0);
double yValue = Math.Round((BoxY * img_Box.Source.Height), 0);
if (mouseDownPos.X < xValue)
{
Canvas.SetLeft(selectionBox, mouseDownPos.X);
selectionBox.Width = xValue - mouseDownPos.X;
}
else
{
Canvas.SetLeft(selectionBox, xValue);
selectionBox.Width = mouseDownPos.X - xValue;
}
if (mouseDownPos.Y < yValue)
{
Canvas.SetTop(selectionBox, mouseDownPos.Y);
selectionBox.Height = yValue - mouseDownPos.Y;
}
else
{
Canvas.SetTop(selectionBox, yValue);
selectionBox.Height = mouseDownPos.Y - yValue;
}
}
Not sure if I fully understand what exactly you are trying to accomplish here but if you just need a border around that text and not to allow the user to move it around with the mouse, set the image to be the background of the Grid and then partition the Grid so that one of the grid cells is directly above that text you want to place the border around. Place the border within that cell and If all the bill images are exactly the same size and have the text in exactly the same location it will always be directly over that text as long as you do not allow users to resize the image... If you want the scrolling just put the entire grid within the ScrollViewer control.
Something like this:
<Grid>
<Grid.Background>
<VisualBrush TileMode="None" >
<VisualBrush.Visual>
<Image Source="{Binding ImageSource}"/>
</VisualBrush.Visual>
</VisualBrush>
</Grid.Background>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="80"/> //Distance from the left edge of the image to the right edge of the border around the "Final Bill" text.
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="150"/> //Distance from the top of the image to the top of the desired location of the border around the "Final Bill" text.
<RowDefinition Height="50"/> //Desired height of the border around the "Final Bill" text.
</Grid.RowDefinitions>
<Border Grid.Column="0" Grid.Row="1" BorderThickness="1"/>
</Grid>

Why my coordinate (1,1) start at (0, 1)?

I overriden Border control and in my overriden OnRender I do:
protected override void OnRender(System.Windows.Media.DrawingContext dc)
{
this.SnapsToDevicePixels = true;
this.VisualEdgeMode = EdgeMode.Aliased;
var myPen = new Pen(new SolidColorBrush(Colors.LightGray), 1);
dc.DrawLine(myPen, new Point(1, 1), new Point(1, RenderSize.Height - 1));
return;
Which give me this result:
Question:
Is anybody can tell me why my code draw a line that start at (0,1) while it is suppose to start at (1, 1) like written in code ?
My DPI are 96, 96.
For ref, this is my xaml:
<Window xmlns:MyControls="clr-namespace:MyControls;assembly=MyControls" x:Class="TestControls.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">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<MyControls:Border3D BorderThickness="3" BorderBrush="Aqua">
<Rectangle Width="74" Height="3" HorizontalAlignment="Left">
<Rectangle.Fill>
<SolidColorBrush Color="#40FF0000">
</SolidColorBrush>
</Rectangle.Fill>
</Rectangle>
</MyControls:Border3D>
<Rectangle Grid.Row="1" Grid.Column="0" Width="80" Grid.ColumnSpan="2" HorizontalAlignment="Left">
<Rectangle.Fill>
<SolidColorBrush Color="LightGray"></SolidColorBrush>
</Rectangle.Fill>
</Rectangle>
</Grid>
</Window>
Keep in mind that (0, 0) is not the center of the upper left pixel. Instead it is the upper left corner of that pixel. In order to draw a line with a stroke thickness of 1 in the middle of the second pixel column (with index 1) you would have to draw from (1.5, 1) to (1.5, RenderSize.Height - 1):
dc.DrawLine(myPen, new Point(1.5, 1), new Point(1.5, RenderSize.Height - 1));
Setting SnapsToDevicePixels = true made your line snap to the left by half a pixel.
If you would use PenLineCap.Square for both the StartLineCap and EndLineCap properties of the line's Pen, you could draw from exactly one pixel center to the other:
var myPen = new Pen(Brushes.LightGray, 1);
myPen.StartLineCap = PenLineCap.Square;
myPen.EndLineCap = PenLineCap.Square;
dc.DrawLine(myPen, new Point(1.5, 1.5), new Point(1.5, RenderSize.Height - 1.5));

how to measure grid width/height in wpf dynamically as the window size changes

What I want to do overall is add an ellipse to a canvas to represent the position of a golf ball on the Circumference of a circle when it is struck at a certain angle by a golf putter from the center of that circle. To do this I am using two images one which is a circle divided up into sections to represent the angle the ball was struck at. The other image is just a putter head that is rotated to represent the angle which the club hits the ball at. This image sits on top of the circle image. The blue ellipse represents the position of the ball.See the image output got from my application in the link below:
Required application output
To do this I basically calculated the width and height of the grid using this code:
public FacePage()
{
InitializeComponent();
GridGraphs.SizeChanged += GridGraphs_SizeChanged;
}
void GridGraphs_SizeChanged(object sender, SizeChangedEventArgs e)
{
GetMeasurements();
}
private void GetMeasurements()
{
GridGraphs.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
double width = GridGraphs.DesiredSize.Width;
double height = GridGraphs.DesiredSize.Height;
RotatePutter(width, height);
}
and then passed the width and height into RotatePutter() where the the width and height was divided by two to get the centre point of the grid see below:
public void RotatePutter(double width, double height)
{
double radius = width * 0.5;
double centerX = width * 0.5;
double centerY = height * 0.5;
Random ran = new Random();
double angle = ran.Next(-15, 15);
if (angle >= 0)
{
if (angle <= 5)
{
lblShotStaus.Content = "Square";
}
else
{
lblShotStaus.Content = "Open";
}
}
else
{
lblShotStaus.Content = "Closed";
}
double angleradians = (angle * (Math.PI / 180));
Point putterCentre = new Point(0.5, 0.5);
imgPutter.RenderTransformOrigin = putterCentre;
RotateTransform rt = new RotateTransform(angle, 0.5, 0.5);
imgPutter.RenderTransform = rt;
BallLocation(radius, angleradians, centerX, centerY);
}
The centre point,radius,angle and the putter Image is rotated here and the radius, angleradians,centerX and centerY are passed to BallLocation to calculate the position of the Ball like so:
public void BallLocation(double rad, double anglerad, double centerX, double centerY)
{
Ellipse ellipse = new Ellipse();
double xBallPoint = (centerX - (rad * Math.Cos(anglerad)));
double yBallPoint = (centerY - (rad * Math.Sin(anglerad)));
ellipse.Height = 10;
ellipse.Width = 10;
ellipse.Fill = new SolidColorBrush(Colors.Aqua);
Canvas.SetLeft(ellipse, xBallPoint);
Canvas.SetTop(ellipse, yBallPoint);
canvasFaceAngle.Children.Add(ellipse);
}
This works ok in full screen put once I change the size of the window the position of the ball for the angle it was hit at is all wrong.
Would anyone have any idea how to dynamically get the grid width and height as the window size changes so to calculate the correct position for the ellipse (ball). Or any other alternative ways of completing this is also welcomed. Thanks in advance.
Here is my xaml:
<UserControl x:Class="HoleMorePuttsApplication.Pages.FacePage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="40*"/>
<ColumnDefinition Width="60*"/>
</Grid.ColumnDefinitions>
<Viewbox Grid.ColumnSpan="1">
<Label Content="Face Angle Page" FontWeight="Bold" />
</Viewbox>
<Grid Name="GridGraphs" Column="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Name="columnLeft" Width="50*"/>
<ColumnDefinition Name="columnRight" Width="50*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Name="rowTop" Height="70*"/>
<RowDefinition Name="rowBottom" Height="30*"/>
</Grid.RowDefinitions>
<Image x:Name="imgAngles" Source="C:\Users\BernardWork\Documents\Work\Net\PuttingApp\Images\360Angles.jpg" Grid.ColumnSpan="2" HorizontalAlignment="Left" VerticalAlignment="Top"/>
<Image x:Name="imgPutter" Source="C:\Users\BernardWork\Documents\Work\Net\PuttingApp\Images\Putter.jpg" Opacity="0.5" Margin="130,105,70,105" Grid.ColumnSpan="2" HorizontalAlignment="Left" VerticalAlignment="Bottom"/>
<Label x:Name="lblShotStaus" Content="Label" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top"/>
<Canvas Name="canvasFaceAngle" Grid.ColumnSpan="2" Grid.Row="0" RenderTransformOrigin="0.543,0.511"></Canvas>
</Grid>
</Grid>
If I understand correct, I believe the problem may be with:
double width = GridGraphs.DesiredSize.Width;
double height = GridGraphs.DesiredSize.Height;
You are calling Measure with no bounds provided which means that it will tell you the size the Grid would be were there no limitations (hence, the DesiredSize). The DesiredSize and the ActualSize may be two very different values.
In the case above, the args of the SizeChanged method will provide you with what you need.
void GridGraphs_SizeChanged(object sender, SizeChangedEventArgs e)
{
RotatePutter(e.NewSize.Width, e.NewSize.Height);
}
So if the goal is just to get the grid width/height, the ActualWidth and ActualHeight properties should give you that information. One thing to be careful about though: while the Grid control changes size along with the window, the Canvas control does not. In other words, if you read the actualwidth/height of a grid, it should correspond to the side of the window, but if you read the actualwith/height of a canvas, it will probably give you incorrect information.

Image crop with custom rectangle

I'm creating app for image scan with WIA. But my images has a lot of unused space if scanned document size is not big. I need to crop that unused space like in Paint with Cross cursor and rectangle. How to do that in WPF?
Code of image cropping is:
private static Image cropImage(Image img, Rectangle cropArea)
{
Bitmap bmpImage = new Bitmap(img);
Bitmap bmpCrop = bmpImage.Clone(cropArea,bmpImage.PixelFormat);
return (Image)(bmpCrop);
}
Put the Image control in a Canvas and add an invisible rectangle to the canvas as well.
On left mouse button down set the top left of the rectangle at the mouse coordinates and make it visible.
On mouse move (and mouse button still down), set the size of the rectangle, so the opposite of the first corner moves with the mouse.
Example:
<Window x:Class="TestWpfApplication.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TestWpfApplication"
Title="MainWindow"
Height="350"
Width="525">
<Canvas MouseLeftButtonDown="Canvas_MouseLeftButtonDown"
MouseMove="Canvas_MouseMove"
Background="AntiqueWhite">
<Image Width="500"
Height="300" />
<Rectangle x:Name="selectionRectangle"
StrokeThickness="1"
Stroke="LightBlue"
Fill="#220000FF"
Visibility="Collapsed" />
</Canvas>
</Window>
And the code behind:
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace TestWpfApplication
{
public partial class MainWindow : System.Windows.Window
{
public MainWindow()
{
InitializeComponent();
}
private void Canvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
var mousePosition = e.GetPosition(sender as UIElement);
Canvas.SetLeft(selectionRectangle, mousePosition.X);
Canvas.SetTop(selectionRectangle, mousePosition.Y);
selectionRectangle.Visibility = System.Windows.Visibility.Visible;
}
private void Canvas_MouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed)
{
var mousePosition = e.GetPosition(sender as UIElement);
selectionRectangle.Width = mousePosition.X - Canvas.GetLeft(selectionRectangle);
selectionRectangle.Height = mousePosition.Y - Canvas.GetTop(selectionRectangle);
}
}
}
}
Note that the Canvas will only respond to a click when it has a color.
Also: you'll need to handle the situation where the user drags the selection right to left and/or bottom to top because that will introduce negative sizes for the width and height of the rectangle. But I'll leave that to you.
Thanks for this code. My actual issues realted to CROP iamge show in another iamge control. How ist possible in WPF. Please fallow this link.
http://social.msdn.microsoft.com/Forums/en-US/302bb1c8-e272-48b1-982d-12cf2aefe8a3/how-to-crop-image-show-in-another-image-control-?forum=wpf
Thank

Categories

Resources