I looked everywhere and haven't come across anything but I would like to know the best way to contain a selection rectangle so that it wouldn't go out of bounds. I have an application were user draws a selection rectangle on top of an image. The rectangle can be also be moved and resized. Currently I just use an exception handler which when an out of range exception is catched it would alert the user. The out of range exception only occurs when moving the drawn rectangle and I would like to make it more streamlined that the actual rectangle cant be dragged or resized outside of the image. Below is the xaml and code behind for my crop control.
Crop Control Code Behind:
public partial class CropControl : UserControl
{
#region Data's
private bool isDragging = false;
private Point anchorPoint = new Point();
private bool MoveRect = false; //flag which intially set to false which means a crop rectangle is not moved but created.
private bool MoveInProgress = false; //flag that is set to true if the crop rect is moving, otherwise false.
private Point LastPoint; // The drag's last point
HitType MouseHitType = HitType.None; //part of the rectangle under the mouse
private enum HitType { None, Body, UL, UR, LR, LL, L, R, T, B }; //Enum for the part of the rectangle the mouse is over.
#endregion
#region Constructor
public CropControl()
{
InitializeComponent();
}
#endregion
#region Dependency Property
//Register the Dependency Property
public static readonly DependencyProperty SelectionProperty =
DependencyProperty.Register("Selection", typeof(Rect), typeof(CropControl), new PropertyMetadata(default(Rect)));
public Rect Selection
{
get { return (Rect)GetValue(SelectionProperty); }
set { SetValue(SelectionProperty, value); }
}
// this is used, to react on changes from ViewModel. If you assign a
// new Rect in your ViewModel you will have to redraw your Rect here
private static void OnSelectionChanged(System.Windows.DependencyObject d, System.Windows.DependencyPropertyChangedEventArgs e)
{
Rect newRect = (Rect)e.NewValue;
Rectangle selectionRectangle = d as Rectangle;
if (selectionRectangle != null)
return;
selectionRectangle.SetValue(Canvas.LeftProperty, newRect.X);
selectionRectangle.SetValue(Canvas.TopProperty, newRect.Y);
selectionRectangle.Width = newRect.Width;
selectionRectangle.Height = newRect.Height;
}
#endregion
private Point lastLoc;
#region MouseLeftButtonDown Event
private void LoadedImage_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
lastLoc = new Point(Canvas.GetLeft(selectionRectangle), Canvas.GetTop(selectionRectangle));
//This statement will enable the creation of a new rectangle only if the mouse left
//button press is outside of a created rectangle and that crop rectangle was initially created.
//This is known since the HitType if outside the rectangle will always be set to None and the crop rect width > 0.
//The previous cropping rect will be removed by setting its value to null.
if (MouseHitType== HitType.None && selectionRectangle.Width>0)
{
selectionRectangle.Width = 0; //set crop rectangle's width to 0
selectionRectangle.Height = 0; //set crop rectangle's height to 0
SetMouseCursor();
MoveRect = false; //flag that crop rectangle is not being moved but drawn.
}
//This statement test if the crop rectangle is not being dragged and moved. If true it would
//set the x and y position of the crop rect in accordance to Canvas. If false it means that
//crop rectangle was already created and is now being moved to different position in the canvas.
if (!isDragging && !MoveRect)
{
anchorPoint.X = e.GetPosition(BackPanel).X; //get the x position of the mouse
anchorPoint.Y = e.GetPosition(BackPanel).Y; //get the y position of the mouse
isDragging = true; //flag that the user is dragging the mouse to create a rectangle
BackPanel.Cursor = Cursors.Cross; //change the cursor to a cross while left button is held down
}
else
{
MouseHitType = SetHitType(selectionRectangle, e.GetPosition(BackPanel)); //get hittype
SetMouseCursor(); //set the mouse cursor based on the hittype
if (MouseHitType == HitType.None) return;
LastPoint = e.GetPosition(BackPanel);
MoveInProgress = true; //flag true since rectangle is being moved
}
}
#endregion
private double CanvasTop, CanvasLeft;
#region MouseMove Event
private void LoadedImage_MouseMove(object sender, MouseEventArgs e)
{
Point offset = new Point((anchorPoint.X-lastLoc.X),(anchorPoint.Y-lastLoc.Y));
var newX=(anchorPoint.X+(e.GetPosition(BackPanel).X)-anchorPoint.X);
var newY=(anchorPoint.Y+(e.GetPosition(BackPanel).Y)-anchorPoint.Y);
CanvasTop = newX - offset.X;
CanvasLeft = newY - offset.Y;
//Statement that checks if crop rect is being created or moved. If moved it will set the
//dimension of the rectanlge and if not it would set the location of the new rectangle.
if (isDragging && !MoveRect)
{
double x = e.GetPosition(BackPanel).X; //get x position of mouse
double y = e.GetPosition(BackPanel).Y; //get y position of mouse
selectionRectangle.SetValue(Canvas.LeftProperty, Math.Min(x, anchorPoint.X)); //set the bottom
selectionRectangle.SetValue(Canvas.TopProperty, Math.Min(y, anchorPoint.Y)); //set the top
selectionRectangle.Width = Math.Abs(x - anchorPoint.X); //set the width
selectionRectangle.Height = Math.Abs(y - anchorPoint.Y); //set the height
if (selectionRectangle.Visibility != Visibility.Visible) //make crop rectangle visible if its not.
selectionRectangle.Visibility = Visibility.Visible;
}
else if (!isDragging && MoveRect)
{
if (!MoveInProgress)
{
MouseHitType = SetHitType(selectionRectangle, e.GetPosition(BackPanel));
SetMouseCursor();
}
else
{
// See how much the mouse has moved.
Point point = e.GetPosition(BackPanel);
double offset_x = point.X - LastPoint.X;
double offset_y = point.Y - LastPoint.Y;
// Get the rectangle's current position.
double new_x = Canvas.GetLeft(selectionRectangle);
double new_y = Canvas.GetTop(selectionRectangle);
double new_width = selectionRectangle.Width;
double new_height = selectionRectangle.Height;
// Update the rectangle.
switch (MouseHitType)
{
case HitType.Body:
new_x += offset_x;
new_y += offset_y;
break;
case HitType.UL:
new_x += offset_x;
new_y += offset_y;
new_width -= offset_x;
new_height -= offset_y;
break;
case HitType.UR:
new_y += offset_y;
new_width += offset_x;
new_height -= offset_y;
break;
case HitType.LR:
new_width += offset_x;
new_height += offset_y;
break;
case HitType.LL:
new_x += offset_x;
new_width -= offset_x;
new_height += offset_y;
break;
case HitType.L:
new_x += offset_x;
new_width -= offset_x;
break;
case HitType.R:
new_width += offset_x;
break;
case HitType.B:
new_height += offset_y;
break;
case HitType.T:
new_y += offset_y;
new_height -= offset_y;
break;
}
// Don't use negative width or height.
if ((new_width > 0) && (new_height > 0))
{
// Update the rectangle.
Canvas.SetLeft(selectionRectangle, new_x);
Canvas.SetTop(selectionRectangle, new_y);
selectionRectangle.Width = new_width;
selectionRectangle.Height = new_height;
// Save the mouse's new location.
LastPoint = point;
}
}
}
}
#endregion
#region MouseLeftButtonUp Event
private void LoadedImage_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
//statement which checks if the mouse left button action is for either creating or
//moving the crop rectangle. If true, isDragging=false since the crop rect is created
//and moverect = true since the created rectangle is ready to be moved.
if (isDragging && !MoveRect)
{
isDragging = false;
if (selectionRectangle.Width > 0)
{
MoveRect = true;
}
}
else
{
MoveInProgress = false; //flags Move in progress as false since rect move action is done.
}
// Set the Selection to the new rect, when the mouse button has been released
Selection = new Rect(
(double)selectionRectangle.GetValue(Canvas.LeftProperty),
(double)selectionRectangle.GetValue(Canvas.TopProperty),
selectionRectangle.Width,
selectionRectangle.Height);
}
#endregion
#region Mutator's
// Return a HitType value to indicate what is at the point.
private HitType SetHitType(Rectangle rect, Point point)
{
double left = Canvas.GetLeft(selectionRectangle);
double top = Canvas.GetTop(selectionRectangle);
double right = left + selectionRectangle.Width;
double bottom = top + selectionRectangle.Height;
//statement that checks if cursor is outside the area of the crop rectangle
//and returns HitType.None.
if (point.X < left) return HitType.None;
if (point.X > right) return HitType.None;
if (point.Y < top) return HitType.None;
if (point.Y > bottom) return HitType.None;
const double GAP = 10; //sets the gap which when mouse over a cursor change is triggered
//statement that checks where the mouse is located within the rectangle.
if (point.X - left < GAP)
{
// Left edge.
if (point.Y - top < GAP) return HitType.UL;
if (bottom - point.Y < GAP) return HitType.LL;
return HitType.L;
}
if (right - point.X < GAP)
{
// Right edge.
if (point.Y - top < GAP) return HitType.UR;
if (bottom - point.Y < GAP) return HitType.LR;
return HitType.R;
}
if (point.Y - top < GAP) return HitType.T;
if (bottom - point.Y < GAP) return HitType.B;
return HitType.Body;
}
// Set a mouse cursor appropriate for the current hit type.
private void SetMouseCursor()
{
// See what cursor we should display.
Cursor desired_cursor = Cursors.Arrow;
switch (MouseHitType)
{
case HitType.None:
desired_cursor = Cursors.Arrow;
break;
case HitType.Body:
desired_cursor = Cursors.ScrollAll;
break;
case HitType.UL:
case HitType.LR:
desired_cursor = Cursors.SizeNWSE;
break;
case HitType.LL:
case HitType.UR:
desired_cursor = Cursors.SizeNESW;
break;
case HitType.T:
case HitType.B:
desired_cursor = Cursors.SizeNS;
break;
case HitType.L:
case HitType.R:
desired_cursor = Cursors.SizeWE;
break;
}
// Display the desired cursor.
if (BackPanel.Cursor != desired_cursor)
BackPanel.Cursor = desired_cursor;
}
#endregion
}
Crop Control XAML:
<UserControl.Resources>
<Storyboard x:Key="MarchingAnts">
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
Storyboard.TargetName="selectionRectangle"
Storyboard.TargetProperty="(Shape.StrokeDashOffset)"
RepeatBehavior="Forever">
<SplineDoubleKeyFrame KeyTime="00:00:00" Value="0"/>
<SplineDoubleKeyFrame KeyTime="00:00:00.3000000"
Value="10"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</UserControl.Resources>
<UserControl.Triggers>
<EventTrigger RoutedEvent="FrameworkElement.Loaded">
<BeginStoryboard Storyboard="{StaticResource MarchingAnts}"/>
</EventTrigger>
</UserControl.Triggers>
<Canvas Name="BackPanel" Background="Transparent" MouseLeftButtonDown="LoadedImage_MouseLeftButtonDown" MouseMove="LoadedImage_MouseMove" MouseLeftButtonUp="LoadedImage_MouseLeftButtonUp">
<Rectangle Name="selectionRectangle" Stroke="#FFFFFFFF"
StrokeThickness="1" StrokeDashOffset="0"
Fill="#220000FF" Visibility="Collapsed"
StrokeDashArray="5"/>
</Canvas>
Sorry for the confusion but i changed my explanation. The rectangle don't go out of bounds when drawn but only happens if the drawn rectangle is moved. Also the exception is caught in my view model's crop method which is shown below:
public void Crop()
{
////Get a copy of the selection in case it changes during execution
Rect cropSelection = Selection;
//// use it to crop your image
Int32Rect rcFrom = new Int32Rect();
rcFrom.X = (int)((cropSelection.X) * (ImagePath.Width) / (ImagePath.Width));
rcFrom.Y = (int)((cropSelection.Y) * (ImagePath.Height) / (ImagePath.Height));
rcFrom.Width = (int)cropSelection.Width;
rcFrom.Height = (int)cropSelection.Height;
try
{
BitmapSource bs = new CroppedBitmap(ImagePath as BitmapSource, rcFrom);
CroppedImage = bs;
}
catch (Exception e)
{
MessageBox.Show("Selection Rectangle is outside the image." + "\n" + "Adjust the cropping rectangle so it's within the boundaries of the Image ", " Error Message", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
Update:
I was able to get it working by calculating the size and position of the selection rectangle against its parent (Canvas) which takes takes the same size as the image. Below is what I added to my code.
double bottom = new_y + selectionRectangle.Height;
double right = new_x+selectionRectangle.Width;
if (new_y< 0)
new_y = 0;
if (new_x < 0)
new_x = 0;
if (bottom > BackPanel.ActualHeight)
new_y = BackPanel.ActualHeight-selectionRectangle.Height;
if (right > BackPanel.ActualWidth)
new_x = BackPanel.ActualWidth - selectionRectangle.Width;
if (new_height > BackPanel.ActualHeight)
new_height = BackPanel.ActualHeight;
if (new_width > BackPanel.ActualWidth)
new_width = BackPanel.ActualWidth;
The new_height and new_width was added because an exception is still thrown if the rectangle occupies the entire image.
If i correctly understood question - you shows message to the user when selection rect goes outside of image rect? If so, why not check that: if new selection state will out of image area - then just not move selection and keep it in old state? I mean - compare borders positions of new selection rect with image rect and make decision: move or not your selection rect in new position (or change or not it's size).
Related
I'm new to Xamarin Android and currently working on a floating action button, I implemented View.IOnTouchListener and normal button click event (faButton.Click += floatButtonPressed;) to carry out my actions. But for the case MotionEventActions.Move, it doesn't work as I wanted. Moving left and right it works fine but for top and bottom it will move downwards a little bit whenever I start moving it. Besides, when I move the button to screen border it will be able to exceed the screen. Hence, I tried detect screen size and restrict it but it still not good enough, is there any other available solution or settings?
public bool OnTouch(View v, MotionEvent e)
{
switch (e.Action)
{
case MotionEventActions.Down:
oldXvalue = e.GetX();
oldYvalue = e.GetY();
if (oldXvalue == e.GetX() && oldYvalue == e.GetY())
{
return false;
}
break;
case MotionEventActions.Up:
if (oldXvalue == e.GetX() && oldYvalue == e.GetY())
{
return false;
}
break;
case MotionEventActions.Move:
var xleft = (int)(e.RawX - oldXvalue);
var xright = xleft + v.Width;
var ytop = (int)(e.RawY - oldYvalue);
var ybtm = (ytop + v.Height);
if (xleft + v.Width >= intWidth)
{
break;
}
if (xleft <= 0)
{
break;
}
if (ytop + v.Height >= intHeight)
{
break;
}
if (ytop <= 0)
{
break;
}
v.Layout(xleft, ytop, xright, ybtm);
break;
}
return true;
}
You can get the screen height and screen width first. And when the button exceed the screen you need to reset the button position.
Try the following code:
public class MainActivity : Activity, IOnTouchListener
{
Button dragAbleBt;
int screenWidth = 0;
int screenHeight = 0;
int lastX = 0, lastY = 0;
public bool OnTouch(View v, MotionEvent e)
{
MotionEventActions ea = e.Action;
switch (ea) {
case MotionEventActions.Down:
lastX = (int)e.RawX;
lastY = (int)e.RawY;
break;
case MotionEventActions.Move:
int dx = (int)e.RawX - lastX;
int dy = (int)e.RawY - lastY;
int left = v.Left + dx;
int right = v.Right + dx;
int top = v.Top + dy;
int bottom = v.Bottom + dy;
if (left < 0)
{
left = 0;
right = left + v.Width;
}
if (right > screenWidth)
{
right = screenWidth;
left = right - v.Width;
}
if (top < 0)
{
top = 0;
bottom = top + v.Height;
}
if (bottom > screenHeight)
{
bottom = screenHeight;
top = bottom - v.Height;
}
v.Layout(left, top, right, bottom);
lastX = (int) e.RawX;
lastY = (int) e.RawY;
v.PostInvalidate();
break;
case MotionEventActions.Up:
break;
}
return false;
}
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView (Resource.Layout.Main);
//DisplayMetrics dm = Resources.DisplayMetrics;
//screenWidth = dm.WidthPixels;
//screenHeight = dm.HeightPixels;
dragAbleBt = FindViewById<Button>(Resource.Id.button1);
dragAbleBt.SetOnTouchListener(this);
}
public override void OnWindowFocusChanged(bool hasFocus)
{
base.OnWindowFocusChanged(hasFocus);
if (hasFocus)
{
Rect outRect = new Rect();
this.Window.FindViewById(Window.IdAndroidContent).GetDrawingRect(outRect);
screenWidth = outRect.Width();
screenHeight = outRect.Height();
}
}
}
I tried detect screen size and restrict it but it still not good enough, is there any other available solution or settings?
You may got the whole screen width and height, the button will exceed the screen height, In this kind of situation you need get the view drawing area by this.Window.FindViewById(Window.IdAndroidContent).GetDrawingRect(outRect)
Screen Shot:
Note: I am using the emulator that is slow, If you are using the real device it will be faster.
I'm building custom control that will display tiles, like colored grid.
I've managed to do drawing, scrolling and basic logic, but I have problem with creating tooltip for each tile.
Each tile color depends on data that is "bound" to that tile.
I'll try to describe my idea:
Above image shows my control, I have 4 squares drawn there, I'd like to show different tooltip when user hovers different parts of my control.
Below is my tooltip (small red rectangle) and lower is standard WinForms Chart component.
I'd like to get this kind of behavior in my control (tooltip is outside of control, so long text is displayed properly).
Below is code of my control with just basic functionality:
public sealed class UC1 : UserControl
{
private bool _showTooltip;
private string _tooltipText;
private Point _mousePosition;
public UC1()
{
MinimumSize = new Size(100, 100);
Size = new Size(100, 100);
SetStyle(ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint | ControlStyles.ResizeRedraw | ControlStyles.OptimizedDoubleBuffer, true);
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
e.Graphics.FillRectangle(Brushes.LightGoldenrodYellow, 0, 0, Width/2, Height/2);
e.Graphics.FillRectangle(Brushes.LightGray, Width/2, 0, Width, Height/2);
e.Graphics.FillRectangle(Brushes.LightSlateGray, 0, Height/2, Width/2, Height);
e.Graphics.FillRectangle(Brushes.LightSteelBlue, Width/2, Height/2, Width, Height);
using (var p = new Pen(Color.Black, 2))
{
e.Graphics.DrawLine(p, Width/2, 0, Width/2, Height);
e.Graphics.DrawLine(p, 0, Height/2, Width, Height/2);
}
if (_showTooltip)
{
SizeF c = e.Graphics.MeasureString(_tooltipText, DefaultFont);
int width = (int) c.Width;
int height = (int) c.Height;
const int offset = 12;
var x = _mousePosition.X + width + offset > Width ? _mousePosition.X - width : _mousePosition.X + offset;
var y = _mousePosition.Y + height + offset > Height ? _mousePosition.Y - height : _mousePosition.Y + offset;
e.Graphics.FillRectangle(Brushes.Red, x, y, width, height);
e.Graphics.DrawString(_tooltipText, DefaultFont, Brushes.Black, x, y);
}
}
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
if (e.X > 0 && e.X < Width/2 && e.Y > 0 && e.Y < Height/2)
{
Debug.WriteLine("1,1 square");
_tooltipText = "1,1";
}
else if (e.X > Width/2 && e.X < Width && e.Y > 0 && e.Y < Height/2)
{
Debug.WriteLine("1,2 square");
_tooltipText = "1,2";
}
else if (e.X > 0 && e.X < Width/2 && e.Y > Height/2 && e.Y < Height)
{
Debug.WriteLine("2,1 square");
_tooltipText = "2,1";
}
else if (e.X > Width/2 && e.X < Width && e.Y > Height/2 && e.Y < Height)
{
Debug.WriteLine("2,2 square");
_tooltipText = "2,2";
}
_mousePosition = e.Location;
_showTooltip = true;
Refresh();
}
protected override void OnMouseLeave(EventArgs e)
{
_showTooltip = false;
Refresh();
base.OnMouseLeave(e);
}
}
I've found similar question: How to make a floating (tooltip) control in Windows.Forms? but I'd like to avoid creating custom tooltip and instead use standard one.
How can I show standard tooltip that will change it text when hovering on different tiles. I'd like to add everything to my user control, so I won't have to add any code to form hosting my control.
Why do you try to draw the ToolTip yourself instead of using the system one?
Just add one to the UC class
// private bool _showTooltip; ?? probably not needed any more..
private string _tooltipText;
// private Point _mousePosition; ??..
ToolTip ttip = new ToolTip();
and set it like this:
// _mousePosition = e.Location; ??..
// _showTooltip = true; ??..
ttip.SetToolTip(this, _tooltipText); // use this in the mousemove
Refresh();
Of course now you can skip the whole Painting part..
If you want to control the location where the ToolTip is shown use one of the ShowToolTip() overloads instead of SetToolTip() ..!
While both are still there this is the result, going over the UC's border and displaying with a nice drop shadow..:
If you really want your ToolTip to look different from the usual ones, you can set its OwnerDraw to true and code its Draw event just like any other control with GDI+ graphics methods..
Update:
There is an inherent flicker problem; for an explanation see Hans' answer here; his recommendation #2 and one of the answers is helpful:
Remember last mouse position and set the tooltip only when the mouse
position changes.
So we need to add a last ToolTip location:
Point tipLoc = Point.Empty;
Which we test and set in the mouse move:
if (tipLoc != e.Location )
{
tipLoc = e.Location;
ttip.SetToolTip(this, _tooltipText);
}
I have the following code:
if (frame != null)
{
canvas.Children.Clear();
_bodies = new Body[frame.BodyFrameSource.BodyCount];
frame.GetAndRefreshBodyData(_bodies);
foreach (var body in _bodies)
{
if (body != null)
{
if (body.IsTracked)
{
// choose which hand to track
string whichHand = "right"; //change to "left" in order to track left hand
Joint handRight = body.Joints[JointType.HandRight];
if (whichHand.Equals("right"))
{
string rightHandState = "-"; //find the right hand state
switch (body.HandRightState)
{
case HandState.Open:
rightHandState = "Open";
break;
case HandState.Closed:
rightHandState = "Closed";
break;
default:
break;
}
canvas.DrawPoint(handRight, _sensor.CoordinateMapper);
}
Here is my DrawPoint code:
public static void DrawPoint(this Canvas canvas, Joint joint, CoordinateMapper mapper)
{
if (joint.TrackingState == TrackingState.NotTracked) return;
Point point = joint.Scale(mapper);
Ellipse ellipse = new Ellipse
{
Width = 20,
Height = 20,
Fill = new SolidColorBrush(Colors.LightBlue)
};
Canvas.SetLeft(ellipse, point.X - ellipse.Width / 2);
Canvas.SetTop(ellipse, point.Y - ellipse.Height / 2);
canvas.Children.Add(ellipse);
}
And Scale:
public static Point Scale(this Joint joint, CoordinateMapper mapper)
{
Point point = new Point();
ColorSpacePoint colorPoint = mapper.MapCameraPointToColorSpace(joint.Position);
point.X *= float.IsInfinity(colorPoint.X) ? 0.0 : colorPoint.X;
point.Y *= float.IsInfinity(colorPoint.Y) ? 0.0 : colorPoint.Y;
return point;
}
The problem that I have is that while the circle does get drawn, it is not drawn over my hand. Instead it stays in the top left corner (0,0), so I'm guessing that it's not getting updated properly. Could anybody tell me what is going on or what the problem is? I would like it to be in the center of my hand (which is being tracked fine because the state of the hand gets updated immediately) and follow my hand as I move it.
The _sensor is my Kinect sensor.
On MouseDownEvent I set upper left corner of Ellipse I'm trying to draw.
public MyCircle(Point location)
{
ellipseObject = new Ellipse
{
Stroke = Brushes.Black,
StrokeThickness = 2,
Margin = new Thickness(location.X, location.Y, 0, 0)
};
}
Then on MouseMoveEvent I update Width and Height properties and it works fine as long as I don't move mouse above or/and to the left of my Ellipse upper left corner, in that case I'm getting exception that these properties can't be negative (which of course makes perfect sense).
public void Draw(Point location)
{
if (ellipseObject != null)
{
ellipseObject.Width = location.X - ellipseObject.Margin.Left;
ellipseObject.Height = location.Y - ellipseObject.Margin.Top;
}
}
The problem doesn't exist with drawing lines:
public void Draw(Point location)
{
lineObject.X2 = location.X;
lineObject.Y2 = location.Y;
}
I know it's trivial, but I'm completely stuck on this. How should I handle drawing Ellipses?
I had this EXACT problem when trying to create a crop tool. Problem is that you need to create if statements for when the cursor goes negative X or negative Y from your starting point. For starters, you'll need to have a global Point that you mark as your 'start' point. Also specify a global current point position that we'll talk about in a minute.
public Point startingPoint;
public Point currentPoint;
Then, make sure you have an onMouseDown event on whatever control you are trying to put the ellipse on.
private void control_MouseDown(object sender, MouseEventArgs e)
{
startingPoint.X = e.X;
startingPoint.Y = e.Y;
}
Then, you need to create if statements in your MouseMove event to check with point (current mouse position, or starting point) has a lower X/Y value
private void control_MouseMove(object sender, MouseEventArgs e)
{
//The below point is what we'll draw the ellipse with.
Point ellipsePoint;
Ellipse ellipseObject = new Ellipse();
currentPoint.X = e.X;
currentPoint.Y = e.Y;
//Then we need to get the proper width/height;
if (currentPoint.X >= startingPoint.X)
{
ellipsePoint.X = startingPoint.X;
ellipseObject.Width = currentPoint.X - startingPoint.X;
}
else
{
ellipsePoint.X = currentPoint.X;
ellipseObject.Width = startingPoint.X - currentPoint.X;
}
if (currentPoint.Y >= startingPoint.Y)
{
ellipsePoint.Y = startingPoint.Y;
ellipseObject.Height = currentPoint.Y - startingPoint.Y;
}
else
{
ellipsePoint.Y = currentPoint.Y;
ellipseObject.Height = startingPoint.Y - currentPoint.Y;
}
ellipseObject.Stroke = Brushes.Black;
ellipseObject.StrokeThickness = 2;
ellipseObject.Margin = new Thickness(ellipsePoint.X, ellipsePoint.Y, 0, 0);
}
Hope this helps!
Save the origin point separately and set the X and Y properties of the Ellipse's Margin to the mouse position and the Width and Height to the distances between the mouse and origin point.
Untested:
public MyCircle(Point location)
{
ellipseObject = new Ellipse
{
Stroke = Brushes.Black,
StrokeThickness = 2,
Margin = new Thickness(location.X, location.Y, 0, 0)
Tag = new Point(location.X, location.Y)
};
}
public void Draw(Point location)
{
if (ellipseObject != null)
{
Point o = (Point)ellipseObject.Tag;
double x = Math.Min(location.X, o.Left);
double y = Math.Min(location.Y, o.Top);
double width = Math.Abs(Math.Max(location.X, o.Left) - x);
double height = Math.Abs(Math.Max(location.Y, o.Top) - y);
ellipseObject.Margin.X = x;
ellipseObject.Margin.Y = y;
ellipseObject.Width = width;
ellipseObject.Height = height;
}
}
I use the following code to draw a square on an inkcanvas at the position of the mouse. But it doesn't draw the shape at the centre of the mouse position, instead slightly to the right and much lower as demonstrated by the following image:
Additionally I would like to stop the pen from drawing when I click to add a shape to the canvas.
How can I correct the positioning and stop the pen drawing?
private void inkCanvas_MouseMove(object sender, MouseEventArgs e)
{
cursorCoords.Content = Mouse.GetPosition(Application.Current.MainWindow);
// Get the x and y coordinates of the mouse pointer.
System.Windows.Point position = e.GetPosition(this);
pX = position.X;
pY = position.Y;
}
private Stroke NewRectangle(double dTop, double dLeft, double dWidth, double dHeight)
{
double T = dTop;
double L = dLeft;
double W = dWidth;
double H = dHeight;
StylusPointCollection strokePoints = new StylusPointCollection();
strokePoints.Add(new StylusPoint(L, T));
strokePoints.Add(new StylusPoint(L + W, T));
strokePoints.Add(new StylusPoint(L + W, T + H));
strokePoints.Add(new StylusPoint(L, T + H));
strokePoints.Add(new StylusPoint(L, T));
Stroke newStroke = new Stroke(strokePoints);
return newStroke;
}
private void inkCanvas_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
if (tool == 3) // shape tool
{
switch (chosenShape)
{
case "square":
Stroke oS = NewRectangle(pY, pX, size, size);
DrawingAttributes attribs = new DrawingAttributes();
attribs.Color = shapeColour;//Colors.LimeGreen;
attribs.Height = 5.0;
attribs.Width = 5.0;
attribs.FitToCurve = false;
oS.DrawingAttributes = attribs;
inkCanvas.Strokes.Add(oS);
break;
}
}
}
In your code this refers to window?
// Get the x and y coordinates of the mouse pointer.
System.Windows.Point position = e.GetPosition(this);
If so then position is the cursor position relatively to the window and not to the inkCanvas
Try
System.Windows.Point position = e.GetPosition(inkCanvas);
If you want to stop canvas from drawing when you select a tool you can switch its IsEnabled property.