Draw rectangle depending where the mouse is - c#

I think I have some difficulty to think through. I have a canvas surrounded by a border and I want to draw a rectangle around the mouse. My problem is, the mouse can move and what I want is like a datagrid. In example, in a datagrid, all cell are already visible, (I know I can make them invisible, that's not the point), but all the cell are static, what I'm trying to do is when I mouseover the canvas, the rectangle be drawned at a static position, sorry if I'm unclear. The picture under should be better.
So as you can see in the first picture my mouse is there and I get some value, what I would like is whenever I'm in the place where I get this value I draw a rectangle over it. As you can see in the second picture when I move my mouse a little bit, I'm still in the area where I have the same value so the rectangle would still be there, and on the third picture I've moved a little further and you see the x coordinate changes and the value also do change then the rectangle would have to be drawned again over the other part
My problem is I don't know how to draw a rectangle of 45 by 40 relative to the mouse but relative to the canvas because if the mouse it's at x : 0 Y : 0 then the rectangle would be 45 by 40 but if my mouse is at X : 10 Y : 10 then the rectangle would have to be, if the mouse is the start point of 10 up the mouse down of 30 and then 10 by left and 35 to the right, I've tried to be as clear as possible, if it's still confuse tell me I will do my best.

You can achieve your requirements easily using the TranslateTransform Class and a couple of double properties:
<Rectangle Name="Rectangle" Stroke="Black" StrokeThickness="1" Fill="{x:Null}">
<Rectangle.RenderTransform>
<TranslateTransform X="{Binding XTransform}" Y="{Binding YTransform}" />
</Rectangle.RenderTransform>
</Rectangle>
You can handle the UIElement.PreviewMouseMove Event on the Canvas element and update your two transform properties from the handler... try something like this:
private void CanvasPreviewMouseMove(object sender, MouseEventArgs e)
{
XTransform = e.GetPosition(Rectangle).X;
YTransform = e.GetPosition(Rectangle).Y;
}

The MouseMove event fires whenever the mouse moves over an element. If I understand your question, you want the know where the mouse is as it moves over the Canvas. So add an event handler to the Canvas. Be sure an name the canvas, you will need the name to refer to is in the mouse event handler.
XAML
<Border Margin='20'
BorderBrush='Orange'
BorderThickness='2'>
<Canvas x:Name='DrawingCanvas'
MouseMove='DrawingCanvas_MouseMove'
Background='Transparent'>
<Rectangle x:Name='MouseRectangle'
Width='30'
Height='30'
Stroke='Purple'
StrokeThickness='2' />
</Canvas>
</Border>
The MouseMove event provides the MouseEventArgs argument. Use it to get the mouse location relative to any element in the UI. In your case, that would be the canvas.
Code
Centers the Rectangle over the mouse tip
private void DrawingCanvas_MouseMove(object sender, MouseEventArgs e) {
Canvas.SetTop(MouseRectangle,
e.GetPosition(DrawingCanvas).Y - MouseRectangle.Height/2);
Canvas.SetLeft(MouseRectangle,
e.GetPosition(DrawingCanvas).X - MouseRectangle.Width /2);
}

Related

How to move a control along with your mouse WPF? [duplicate]

This question already has answers here:
Make Object Follow Mouse On MouseDown and "Stick" On MouseUp
(3 answers)
Closed 2 years ago.
I am making a Space Invaders clone where you control your ship with your mouse. I found online solutions for dragging and dropping a control, which worked but I couldn't manage to translate it to just moving the control with only your mouse.
private void Window_MouseMove(object sender, MouseEventArgs e)
{
Mothership.Body.Margin = new Thickness(Mouse.GetPosition(gridSpace).X, Mouse.GetPosition(gridSpace).Y, 0, 0);
}
To clarify, I want the ship's(Image control) location to be exactly where to cursor is, without an offset. I don't even know where this offset comes from or how to get it.
Here is a screenshot of my mouse moving, you can see the image itself it very far away from the actual cursor. How do I fix this? If you need more information I will try to provide it. image
I guess that yours image control inside the grid have HorizontalAlignment="Stretch" and VerticalAlignment="Stretch" (The default values), that cause the unwanted offset. So as Clemens suggested you must use a canvas. otherwise if you can't change your parent control set the image HorizontalAlignment="Left" and VerticalAlignment="Top" and your code will be work. But as the Clemens and Ostas suggested moving a object by margin is not a good way and if you must use grid you can use a TranslateTransform (also see this).
So just set RenderTransform property of image control and set it's X and Y properties in Window_MouseMove.
Sample xaml:
<Grid x:Name="gridSpace">
<Image x:Name="Body" Source="*.png" Width="100" Height="100" HorizontalAlignment="Left" VerticalAlignment="Top">
<Image.RenderTransform>
<TranslateTransform x:Name="mTransform"/>
</Image.RenderTransform>
</Image>
</Grid>
Sample code:
private void Window_MouseMove(object sender, MouseEventArgs e)
{
//Body.Margin = new Thickness(Mouse.GetPosition(gridSpace).X, Mouse.GetPosition(gridSpace).Y, 0, 0);
Point pos = e.GetPosition(gridSpace);
mTransform.X = pos.X;
mTransform.Y = pos.Y;
}

How to continue scaling the image visually?

I have a ScrollViewer with Image in it. I attached listener to MouseWheel event so I could zoom in/out the image. The code looks like this:
private void ImageMouseWheel(object sender, MouseWheelEventArgs e)
{
Matrix matrix = image.LayoutTransform.Value;
if (e.Delta > 0)
matrix.ScaleAt(1.5, 1.5, e.GetPosition(image).X, e.GetPosition(image).Y);
else
matrix.ScaleAt(1.0 / 1.5, 1.0 / 1.5, e.GetPosition(image).X, e.GetPosition(image).Y);
image.LayoutTransform = new MatrixTransform(matrix);
}
It pretty much works, but it has odd quirk -- when the image is so zoomed out that is at state "fit to parent" visually I cannot it zoom out more (visually), but the matrix is scaled still.
This is bad in sense that any computation after this point is wrong, and also it has strange effect in UI, say user starts from "fit to parent" zoom:
zoom out --> visually no change, internally matrix is zoomed out
zoom in --> surprise, visually no change, internally matrix is zoomed in
zoom in --> visually zoom in, matrix is also changed (OK)
Because of those problems I would like either to continue zooming out visually in order to keep both things (internal and visual) in sync.
How to achieve this?
Xaml:
<DockPanel>
<ScrollViewer Name="imageScrollViewer" Grid.Column="1" MouseWheel="ImageMouseWheel"
CanContentScroll="true" HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto">
<Image MouseWheel="ImageMouseWheel" Name="image" Source="{Binding Path=Bitmap}"
MouseMove="Image_MouseMove"></Image>
</ScrollViewer>
</DockPanel>
Update: Originally I asked how to continue scaling visually or stop internally, but I found out that the last step of zooming out, when the image hits the state "fit to parent" is already out of sync internally-visually. For example position of the mouse is not correctly decoded above such image when moving the mouse around. Only when I zoom in a little the positions are correct. So the only way I see is to scale it visually correctly.
Hmm, I am tempted to delete this embarrassing question, but on the other hand maybe someone will have the same problem in future.
The problem is image is zoomed out, but later the dock panel zoom it in back. The image does not know about it so the math goes astray. The solution is to add some container which does not do additional scaling on itself, like stack panel.
Thus the solution lies in xaml:
<DockPanel>
<StackPanel>
...
</StackPanel>
</DockPanel>

Trying to animate such that the other rectangle below it moves upward?

<StackPanel x:Name="rootStackPanel" Background="{ThemeResource SystemControlAcrylicWindowBrush}" Padding="0,48">
<Rectangle x:Name="sampleRectangle" Width="200" Height="300" Fill="DeepPink" DoubleTapped="Rectangle_DoubleTapped" RenderTransformOrigin="0.5,0.5">
<Rectangle.RenderTransform>
<CompositeTransform TranslateY="-100"/>
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle x:Name="otherRectangle" Width="200" Height="200" Fill="Cyan"/>
</StackPanel>
I want to achieve a dynamic look, where, when the pink rectangle is moved upward, the blue rectangle takes up the left over space. Such that it is always touching the pink rectangle.
I have tried manipulating offsets and scale properties provided by visual layer but the actual height is not affected by any of the property, as a result the other rectangle just stays in the original position. Now I am trying to achieve that effect using storyboards animation. But as you can see, the translation property (and the scale property as well) doesn't effect the actual container of the control somehow but rather makes the content in it move to whatever translation.
So, what properties do I need to manipulate to achieve the effect where the other rectangle inside the stackpanel moves dynamically with the changes to the first rectangle?
I know adjusting width or height property would result in what I am trying to achieve but for more complex scenarios where there might be a textbox rather than simple rectangle that is being animated, there is sort of a weird animation of the placeholder text(shrinking of text), which is something I don't want!
So, what properties do I need to manipulate to achieve the effect where the other rectangle inside the stackpanel moves dynamically with the changes to the first rectangle?
You could use Microsoft.Toolkit.Uwp.UI.Animations.Behaviors to realize this feature directly. Before animate your rectangle, you need to get the absolute position of the each rectangle like the follow.
var scgt = sampleRectangle.TransformToVisual(Window.Current.Content);
Point screenCoords = scgt.TransformPoint(new Point(0, 0));
var ddv = otherRectangle.TransformToVisual(Window.Current.Content);
Point Res = ddv.TransformPoint(new Point(0, 0));
If you want pink rectangle to move upward, you could Offset offsetY value
sampleRectangle.Offset(offsetX: 0, offsetY: -(float)screenCoords.Y - (float)sampleRectangle.Height, duration: 2500).Start();
And then animate otherRectangle like the follow
otherRectangle.Offset(offsetX: -(float)Res.X, offsetY: -(float)Res.Y).Scale(scaleX: 2, scaleY: 2).Start();
You need keep scaleX equal with scaleY when you scale TextBox.Otherwise, the TextBox will be deformed.

Rotating cursor without using WinForms

Is it possible to rotate FrameworkElement.Cursor?
My application allows user to rotate objects around their center. Once rotated, default resize cursors appear awkward on top of tilted borders.
My first thought was to apply RotateTransform to the Cursor property, but it looks like we can't do that in XAML. Next I tried inheriting from Cursor class, but it looks like MS guys have sealed it.
Another way is to set default cursor to None and use my own image (with transform) and set its position upon MouseMove. I'm not willing to go down that path if there are easier alternatives. Anyone with a good suggestion?
I'm looking for a WPF-only solution if at all possible.
Finally managed it within the bounds of WPF, without using WinForms or PInvokes. Instead of creating custom cursors (*.cur) on the fly or converting Visuals into cursors, I used MouseMove event of the parent control along with a WPF element (Path) as my cursor. Here's the way just in case anyone is interesed:
Set the Cursor of your resize thumb (or whatever you're using as the border of your shape) to None, so that WPF doesn't display default arrow.
Create your own cursor. Could be any FrameworkElement, but I have used Path for its easy manipulation to create any shape you want. Note that most of the properties I have set below are important.
<Path x:Name="PART_EW"
Data="M0,20 L25,0 25,15 75,15 75,0 100,20 75,40 75,25 25,25 25,40z"
Fill="White" Stroke="Black" StrokeThickness="1" Visibility="Collapsed"
Width="50" Height="20" Opacity=".7" Stretch="Fill" Panel.ZIndex="100001"
HorizontalAlignment="Left" VerticalAlignment="Top" IsHitTestVisible="False"
/>
Add the following code in your resize thumb:
protected override void OnMouseEnter(MouseEventArgs e)
{
base.OnMouseEnter(e);
var Pos = e.GetPosition(this);
PART_EW.Margin = new Thickness(
Pos.X - PART_EW.Width / 2,
Pos.Y - PART_EW.Height / 2,
-PART_EW.Width,
-PART_EW.Height);
PART_EW.Visibility = Visibility.Visible;
}
protected override void OnMouseLeave(MouseEventArgs e)
{
base.OnMouseLeave(e);
PART_EW.Visibility = Visibility.Collapsed;
}
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
var Pos = e.GetPosition(designerItem);
PART_EW.Margin = new Thickness(
Pos.X - PART_EW.Width / 2,
Pos.Y - PART_EW.Height / 2,
-PART_EW.Width,
-PART_EW.Height);
}
Note that I've not set RotateTransform of my Path anywhere in the code, since it is already part of the resize thumb and therefore acquires the angle of the parent control automatically.
Hope this helps people down the road.

Move image with mouse wpf c#

Hello, I have been trying to work this problem out for some time now,
I hope that you are able to lead me in the right direction!
The goal is to have a "flashlight" effect in my wpf game, to do this I have pursued 2 main options.
Have a black png image that is 200% width & height of window with a hole in the center, then move the image with the mouse
Have a black image the size of the window, and have an opacity mask element that is a circle shape that looks through the black layer to the content below. This circle moves with the mouse.
I cannot get either of these options to work, in wpf when you have an opacity mask it appears you cannot move the element that is "punching through" the layer. And for the large image method, I cannot convert a mouse Point object to an image's position. Below are some potential methods of getting mouse position, I have not been able to move the image to "point" location.
Point to screen
private void MouseCordinateMethod(object sender, MouseEventArgs e)
{
var relativePosition = e.GetPosition(this);
var point= PointToScreen(relativePosition);
_x.HorizontalOffset = point.X;
_x.VerticalOffset = point.Y;
}
GetPosition
var point = e.GetPosition(this.YourControl);
The final game will be using kinect for controls, so if there is a way to do this with kinect libraries or natively that is also an option. Thank you.
You could use a Path element with a CombinedGeometry consisting of a very large RectangleGeometry and an excluded (and therefore transparent) EllipseGeometry, which is moved by changing its Center property on mouse input:
<Grid MouseMove="Grid_MouseMove">
<TextBlock Text="Hello, World" FontSize="40"
HorizontalAlignment="Center" VerticalAlignment="Center"/>
<Path Fill="Black">
<Path.Data>
<CombinedGeometry GeometryCombineMode="Exclude">
<CombinedGeometry.Geometry1>
<RectangleGeometry Rect="0,0,10000,10000"/>
</CombinedGeometry.Geometry1>
<CombinedGeometry.Geometry2>
<EllipseGeometry x:Name="circle" RadiusX="100" RadiusY="100"/>
</CombinedGeometry.Geometry2>
</CombinedGeometry>
</Path.Data>
</Path>
</Grid>
The MouseMove handler:
private void Grid_MouseMove(object sender, MouseEventArgs e)
{
circle.Center = e.GetPosition((IInputElement)sender);
}

Categories