Change cursor to image in WPF. - wpf

I am trying to implement a "stamping" feature. I am selecting an image from a list, and using it to stamp on the clicked position on my canvas.
I have read several solutions on how to change the cursor, but they involved simply changing the ico texture.
I want to be able to preview in real time what I will be stamping. So if I change the rotation of the stamp, the cursor needs to rotate appropriately, if I scale the stamp the cursor needs to be scaled, and if I switch the stamp the cursor needs to switch.
I tried adding an image to an observablecollection, and binding it to the canvas. Then I tried updating the position, image, scale inside the MouseMove event of the canvas, but it doesnt work.
Here is the MouseMove function:
private void Canvas_MouseMove(object sender, MouseEventArgs e)
{
if (currentTool == "staticBrush" && lvDataBinding.SelectedIndex != -1)
{
canvasImages[0].Name = srcImages[lvDataBinding.SelectedIndex].Name;
canvasImages[0].BmpImage = new BitmapImage(new Uri(canvasImages[0].Name, UriKind.Relative));
scale(canvasImages[0]);
canvasImages[0].OffsetX = e.GetPosition(canvasDataBinding).X;
canvasImages[0].OffsetY = e.GetPosition(canvasDataBinding).Y;
}
}

You have two main options... you can either follow a fairly complex tutorial such as WPF Tutorial - How To Use Custom Cursors on TechPro, which should enable you to create a Cursor from just about any WPF UIElement, or you can simply hide the Cursor by setting Cursor = Cursors.None and replacing it with your own Image... of course, with this method, you'd also be responsible for moving the Image wherever the mouse cursor moves.

Related

WPF TouchDevice get absolute coordinates

I try to create a window, that can be moves or resized via multi-touch gestures. I tried it this way. I captures the TouchDown-Event of the window and saved all active TouchDevices in a List, to know which TouchDevices are active. I catch updated and deactivated event of the TouchDevices to know when they are moved and when they are deactivated. I save the Left and Top Property of the Window and the position where the TouchDevice started and everytime the Updated event is called I move the Window to the new Position relative to the new position of the TouchDevice. This works if I move the finger. But if I don't move the finger (or just very little) the window suddenly began shaking (moving chaotically) and then soon disappears to a position outside of the screen.
I think the problem here is, that the function "GetTouchPoint" of the TouchDevice only give relative coordinates related to the window (even if I set the parameter null instead if the window reference). And because the Window moves the relative position of the TouchDevice (that doesn't move) changes too. So I did a research but wasn't able to find a way to determine the screen coordinates of the touch device.
So I hope anyone can help me how to get the absolute coordinates of the TouchDevice. Or can help me find another way to "DragMove" the window with touch.(I tried DragMove, but that only works for mouse clicks, not TouchDowns) Also I like to resize the window when two Touch Devices are active and therefor I also need absolute coordinates because otherwise same effect happens.
I ran into this issue because my taskbar is on the right edge of the screen, effectively pushing the maximized window to the right. This issue also arises when the application's window is not maximized, and floating somewhere on the screen.
Here is an extension method that fixes the coordinates based on the application window's position.
public static Point FixCoordinates(this Point point)
{
var left = Application.Current.MainWindow.Left;
var top = Application.Current.MainWindow.Top;
return new Point(point.X + left, point.Y + top);
}
You may want to pass in a window that hosts your touched control as a parameter. In my case it is application's main window.
Also, since you tagged the question with "multi-touch", here is a method which averages multiple touch coordinates:
public static Point GetAverage(this IEnumerable<Point> points)
{
var averageX = points.Average(p => p.X);
var averageY = points.Average(p => p.Y);
return new Point(averageX, averageY);
}
And I use it in the code like this:
private void TouchAdornment_TouchDown(object sender, TouchEventArgs e)
{
var touchPosition = (sender as UIElement).TouchesOver.Select(t => t.GetTouchPoint(null).Position).GetAverage().FixCoordinates();
}

How to dynamically resize a label inside a WPF application?

I need to know how I could dynamically resize a label in a WPF application.
I've already found a sample in this article which has already achieved both dragging and resizing a label at the same time.
I dug into the code, and to make it short, I found out that inside OnMouseMove event of the label, it checks the mouse cursor shape and if it was Hand it will do the dragging and if it was either of the resizing arrows it will do the resizing correspondingly.
Check it out. you'll see.
In this certain example I couldn't manage to find out how the cursor shape changes to resizing arrows when the mouse is hovered on the label's border.
So
I either need to find out 'how I could change the mouse cursor shape to resizing arrows when hovered on a label's border', OR to find a new approach to resize a label, dynamically.
Changing the cursor is done via the this.Cursor property.
I opened the code in the article and saw how they do it...
In the OnMouseMove the cursor is changed if the left mouse button is NOT clicked:
Point currentLocation = e.MouseDevice.GetPosition(wnd);
......
......
const int dragHandleWidth = 3;
var bottomHandle = new Rect(0, height - dragHandleWidth, width, dragHandleWidth);
var rightHandle = new Rect(width - dragHandleWidth, 0, dragHandleWidth, height);
Point relativeLocation = wnd.TranslatePoint(currentLocation, this);
if (rightHandle.Contains(relativeLocation))
{
this.Cursor = Cursors.SizeWE;
}
else if (bottomHandle.Contains(relativeLocation))
{
this.Cursor = Cursors.SizeNS;
}
else
{
this.Cursor = Cursors.Hand;
}
In other words, they check if the current mouse location is within 3 px of the bottom or right border, if it is, they change the cursor accordingly...
You can easily change this logic to suite your needs....

How to implement my own scrolling in windows form

I have an MdiClient derived from Form and I use the surface of this control for GDI+ drawing. I run into troubles implementing my own scrolling for this control. I set both AutoScroll and AutoSize properties to false and try to use form's own horizontal/vertical scrollbars instead of placing my own. Observed form's behavior is quite confusing. To begin with there are two properties (A) HScroll and (B) HorizontalScroll that also allows access to Visible attribute.
I ended up setting HorizontalScroll.Visible = true and leaving HScroll = false (same for vertical) but am curious why there are two of them. Documentation implies that both control visibility of horizontal scroll bar but they do not appear to access the same data. Besides, it looks like HScroll is being reset on every paint. At the moment I ignore existence of HScroll/VScroll. Is it OK for my application?
What is more critical for me is the ability control placement of the thumb on scroll bars. I set VerticalScroll attributes Minimum = 0, Maximum = 100, and Value = 50 but when form is displayed thumb is positioned at the start of scrollbar not in the middle. Why? Also when user clicks on horizontal scrollbar an event handler for horizontal scrolling is invoked but meanwhile form has already reset VerticalScroll.Value to 0 (without raising vertical scroll event). What is going on?
I probably don't understand how framework expects me to implement what I need. Can someone shed some light.
Credit goes to LarsTech who pointed me towards good solution. Setting large AutoScrollMinSize automatically does whatever needs to be done enabling and controlling form scrollbars. There is one potential trap to watch for. Be aware that programmatic attempts to set AutoScrollPosition will be ignored until form is shown. So if you want your form to open with scrollbars not in the default (0,0) position then place your code inside form_shown event handler.
Just set the AutoScrollMinSize to your desired canvas.
Quick example:
using System.Drawing;
using System.Drawing.Drawing2D;
private void Form1_Load(object sender, EventArgs e)
{
this.AutoScrollMinSize = new Size(1200, 1200);
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.Clear(SystemColors.Window);
using (Matrix mx = new Matrix(1, 0, 0, 1, this.AutoScrollPosition.X, this.AutoScrollPosition.Y))
{
e.Graphics.Transform = mx;
e.Graphics.FillEllipse(Brushes.Red, new Rectangle(250, 250, 100, 100));
}
}
See these links: Understanding Windows Forms AutoScroll and How to back-track the mouse to the virtual page.

Dynamic drawing of opacity mask

I've got two background pictures, one is blurry, the other one is the same picture, but more colorful.
The default background image is the blurry one.
When I move the cursor, I'd like to change the background image from the blurry to the colorful one,
but only in a circle around the cursor,
and when I move the cursor forward, the changed background stays where the circle around the cursor went earlier.
(like when you scratch a lottery ticket with a coin)
I think I have to handle the MouseMove event, and use the MouseEventArgs cursor position,
but I cannot go through, and I really need help!
Thanks in advance!
You might want to try following this path:
Add a Canvas to your page, with the same size as both of your images
Create a clipping path in the shape of an ellipse (<Ellipse ...>) and position it outside of your image, in the canvas
Put the "blurry image" on your canvas first (below), and then the "sharp image", filling the whole canvas.
Let the ellipse be the clipping mask of your "sharp image" (using Image.Clip or YourUIElement.Clip (reference on MSDN)
Move your ellipse with the mouse cursor. The code might look like this (note: I didn't test the code):
-
imageCanvas.MouseMove += imageCanvas_MouseMove;
private void imageCanvas_MouseMove(object sender, MouseEventArgs e)
{
Point mousePosition = e.GetPosition();
Canvas.SetTop(myEllipse, mousePosition.Y - myEllipse.ActualHeight / 2);
Canvas.SetLeft(myEllipse, mousePosition.X - myEllipse.ActualWidth / 2);
}
If this works, you can increment your visual design adding animations on MouseEnter/MouseLeave, etc.

WPF - simulate mouse events on off-screen rendered Grid

I'm rendering a WPF grid with multiple elements (buttons, textbox, ...) to a bitmap which is then used as a texture for a 3D surface in a Direct3D scene. For user interaction I create a 3D ray from the 2D mouse cursor position into the 3D scene finding the intersection point with the gui surface. So I know where the user has clicked on the WPF grid, but from there I'm stuck:
How can I simulate mouse events on the WPF elements while they are not actually shown in an open window but rendered off-screen?
Recently, I was looking into UIAutomation and RaiseEvent but these are used to send events to individual elements, not the whole visual tree. Traversing the tree manually and looking for elements at the cursor position would be an option but I haven't found a way to do this accurately. VisualTreeHelper.HitTest is a good start but instead of finding TextBox it finds TextBoxView and instead of ListBox it finds a Border.
EDIT: returning HitTestResultBehavior.Continue in the result callback of HitTest lets me walk through all elements at a given point. I can now send mouse events to all these elements but the values of the MouseEventArgs object are those of the real mouse. So I have to create a custom MouseDevice which apparently is impossible.
PointHitTestParameters p = new PointHitTestParameters(new Point(
((Vector2)hit).X * sourceElement.ActualWidth,
(1 - ((Vector2)hit).Y) * sourceElement.ActualHeight));
VisualTreeHelper.HitTest(sourceElement,
new HitTestFilterCallback(delegate(DependencyObject o)
{
return HitTestFilterBehavior.Continue;
}),
new HitTestResultCallback(delegate(HitTestResult r)
{
UIElement el = r.VisualHit as UIElement;
if (el != null)
{
MouseButtonEventArgs e = new MouseButtonEventArgs(Mouse.PrimaryDevice, 0, MouseButton.Left);
if (leftMouseDown) e.RoutedEvent = Mouse.MouseDownEvent;
else e.RoutedEvent = Mouse.MouseUpEvent;
el.RaiseEvent(e);
}
return HitTestResultBehavior.Continue;
}), p);
You might be able to send windows messages (like WM_MOUSEMOVE) to the WPF window's HWND, via the PostMessage(..) win32 method. I believe these messages would be read by WPF and executed as if it came from a user.
If you are feeling really brave, you can try my hack to get access to the WPF IDirect3DDevice9Ex. From there you can copy the backbuffer in the swapshchain to your d3d scene. If you are using 9Ex (Vista D3D9), you can take advantage of the shared resources (share surfaces/textures/etc between devices and processes) feature to help with performance.
You may still have to do some trickery with windows messages for interactivity.

Resources