I'm still learning WPF but what I'm trying to do is make it so when the image is clicked it follows the mouse around. So far I have the events firing properly, but I don't think I'm relocating the image properly.
<Image x:Name="LetterBlock" HorizontalAlignment="Left" Height="100" VerticalAlignment="Top" Width="100" MouseDown="ImageOnMouseDown" MouseMove="ImageOnMouseMove">
<Image.Source>
<ImageSource>pack://application:,,,/Resources/A_LetterBlock.jpg</ImageSource>
</Image.Source>
<Image.Margin >
<Thickness>145,104,0,0</Thickness>
</Image.Margin>
</Image>
And this is my event handlers. I probably have some irrelevant code in there, but I can clean that up once I get it working.
public void ImageOnMouseDown(Object sender, MouseEventArgs e) {
IsClicked = true;
}
public void ImageOnMouseMove(Object sender, MouseEventArgs e)
{
if (!IsClicked)
return;
Point position = GetMousePositionWindowsForms();
Thickness nMargin = new Thickness(position.X, position.Y, 0, 0);
LetterBlock.Margin = nMargin;
}
private Point GetMousePositionWindowsForms()
{
System.Drawing.Point point = System.Windows.Forms.Control.MousePosition;
return new Point(point.X, point.Y);
}
The problem is that when I click on the image, it disappears...
//note I just took out InvalidateVisual and it still doesn't work, but the image jumps off the screen now.
//note2 I added some code to maximize the window and that helped. I learned that the image is running away from my mouse. It seems the mousemove event is only firing when the mouse is ontop of the image and immediately stops after its off of the image so it's kinda like it's running away from the mouse...
Try this. Use the Mouse.GetPosition(this) to get the position of the mouse pointer relative to the Window with the upper-left corner of the Window being the point of origin..
using System;
using System.Windows;
using System.Windows.Input;
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void LetterBlock_MouseDown(object sender, MouseButtonEventArgs e)
{
IsClicked = !IsClicked;
if (IsClicked)
{
LetterBlock.CaptureMouse();
}
else
{
LetterBlock.ReleaseMouseCapture();
}
}
private void LetterBlock_MouseMove(object sender, MouseEventArgs e)
{
if (!IsClicked)
return;
Point position = Mouse.GetPosition(this);
Thickness nMargin = new Thickness(position.X, position.Y, 0, 0);
LetterBlock.Margin = nMargin;
//InvalidateVisual();
}
public bool IsClicked { get; set; }
}
Related
I am working on a program that displays the liveView image from a Nikon camera in a pictureBox. I want to be able to hover with the cursor over the image and display a zoomed in area around the cursor in another picturebox. I would also like to add a crosshair instead of mouse pointer. The only solution I have found so far is the following:
zoom an image in a second picturebox following cursor
It does exactly what I want, however I can not get it to work. More specifically, nothing is showing up in picZoom. In the example, images are loaded while in my case, a video stream is shown. That might be the reason why I am not getting it to work. I am relatively new to c#, and did not fully understand the example.
Lets say I have picBox where I receive the video stream. How do I show a portion of picBox around the cursor (let's say a rectangle around the cursor of dimensions x,y) in picZoom with a crosshair as in the example. I do not need to worry about differing dimensions of picBox and picZoom since they will not vary. I also want to be able to vary the degree of zoom by a factor zoomFactor.
If anyone could give me pointers or a solution, it would be greatly appreciated. Also, sorry if my question is poorly formatted, I am new to the forum.
Thank you!
Alexander
I would approach it like this
using System;
using System.Drawing;
using System.Windows.Forms;
namespace MagnifierExample
{
class Magnifier : PictureBox
{
public Magnifier()
{
Visible = false;
}
PictureBox source;
private Point sourcePoint;
Cursor oldCursor;
public PictureBox Source
{
get
{
return source;
}
set
{
if (source != null)
{
source.MouseEnter -= Source_MouseEnter;
source.MouseLeave -= Source_MouseLeave;
source.MouseMove -= Source_MouseMove;
source.Cursor = oldCursor;
}
source = value;
if (source != null)
{
source.MouseEnter += Source_MouseEnter;
source.MouseLeave += Source_MouseLeave;
source.MouseMove += Source_MouseMove;
oldCursor = source.Cursor;
source.Cursor = Cursors.Cross;
}
}
}
private void Source_MouseEnter(object sender, EventArgs e)
{
Visible = true;
BringToFront();
}
private void Source_MouseLeave(object sender, EventArgs e)
{
Visible = false;
}
private void Source_MouseMove(object sender, MouseEventArgs e)
{
sourcePoint = e.Location;
Location = new Point(source.Location.X + e.X - Width / 2, source.Location.Y + e.Y - Height / 2);
Invalidate();
}
protected override void WndProc(ref Message m)
{
const int WM_NCHITTEST = 0x0084;
const int HTTRANSPARENT = (-1);
if (!DesignMode && m.Msg == WM_NCHITTEST)
{
m.Result = (IntPtr)HTTRANSPARENT;
}
else
{
base.WndProc(ref m);
}
}
protected override void OnPaint(PaintEventArgs pe)
{
if (!DesignMode && Source != null && Source.Image != null)
{
Rectangle destRect = new Rectangle(0, 0, Width, Height);
// IMPORTANT: This calculation will depend on the SizeMode of the source and the amount you want to zoom.
// This does 2x zoom and works with PictureBoxSizeMode.Normal:
Rectangle srcRect = new Rectangle(sourcePoint.X - Width / 4, sourcePoint.Y - Height / 4, Width / 2, Height / 2);
pe.Graphics.DrawImage(Source.Image, destRect, srcRect, GraphicsUnit.Pixel);
}
else
{
base.OnPaint(pe);
}
}
}
}
To hook it up all you have to do is add a Magnifier to your form, give it a size, and set its Source to the PictureBox you want it to magnify.
this.magnifier1.Source = this.pictureBox1;
I used the approach in this answer to ignore messages from the magnifier window.
Importantly, I only coded up the zoom math for the PictureBoxSizeMode.Normal SizeMode of the source PictureBox. But I think this is a pretty good start.
Not 100% sure what you wanted for crosshairs, but this uses the built-in crosshair cursor.
I am struggling with making both touch events and manipulation work properly in a WPF project. I have a ScrollViewer which contains a picture and I would like to scroll both horizontally and vertically using a swipe gestures. Additionally, I would like to zoom in/out in the center of the pinch gesture. The code below achieves what I wish, but it has the following problems:
Sometimes the scrolling is laggy;
The scrolling does not work on the first try, only when attempting the same gesture a second time;
The zoom in/out does not work on the first try, only when attempting the same gesture a second time.
I enabled the IsManipulationEnabled and I implemented the code for zoom in/out functionality. However, I was not able to combine it with the scrolling functionality (by setting the PanningMode in the ScrollViewer only). Therefore, I created a custom control which inherits from Image control and I overwritten the OnTouchDown and OnTouchUp event handlers. Basically, what I am doing in these overwritten handlers is counting the number of touches on the screen and enabling/disabling manipulation. I also tried setting the PanningMode for the ScrollViewer, but it did not do the trick.
Below is the XAML:
<Grid>
<ScrollViewer
x:Name="ScrollViewerParent"
HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto"
PanningMode="Both">
<local:CustomImage
x:Name="MainImage"
Source="{Binding Source={x:Static local:Constants.ImagePath}}"
IsManipulationEnabled="True"
ManipulationStarting="MainImage_ManipulationStarting"
ManipulationDelta="MainImage_ManipulationDelta">
</local:CustomImage>
</ScrollViewer>
</Grid>
Here is the code-behind:
public partial class MainWindow : Window
{
private void MainImage_ManipulationStarting(object sender, ManipulationStartingEventArgs e)
{
e.ManipulationContainer = ScrollViewerParent;
e.Handled = true;
}
private void MainImage_ManipulationDelta(object sender, ManipulationDeltaEventArgs e)
{
var matrix = MainImage.LayoutTransform.Value;
Point? centerOfPinch = (e.ManipulationContainer as FrameworkElement)?.TranslatePoint(e.ManipulationOrigin, ScrollViewerParent);
if (centerOfPinch == null)
{
return;
}
var deltaManipulation = e.DeltaManipulation;
matrix.ScaleAt(deltaManipulation.Scale.X, deltaManipulation.Scale.Y, centerOfPinch.Value.X, centerOfPinch.Value.Y);
MainImage.LayoutTransform = new MatrixTransform(matrix);
Point? originOfManipulation = (e.ManipulationContainer as FrameworkElement)?.TranslatePoint(e.ManipulationOrigin, MainImage);
double scrollViewerOffsetX = ScrollViewerParent.HorizontalOffset;
double scrollViewerOffsetY = ScrollViewerParent.VerticalOffset;
double pointMovedOnXOffset = originOfManipulation.Value.X - originOfManipulation.Value.X * deltaManipulation.Scale.X;
double pointMovedOnYOffset = originOfManipulation.Value.Y - originOfManipulation.Value.Y * deltaManipulation.Scale.Y;
double multiplicatorX = ScrollViewerParent.ExtentWidth / MainImage.ActualWidth;
double multiplicatorY = ScrollViewerParent.ExtentHeight / MainImage.ActualHeight;
ScrollViewerParent.ScrollToHorizontalOffset(scrollViewerOffsetX - pointMovedOnXOffset * multiplicatorX);
ScrollViewerParent.ScrollToVerticalOffset(scrollViewerOffsetY - pointMovedOnYOffset * multiplicatorY);
e.Handled = true;
}
}
The XAML for the custom control:
<Style TargetType="{x:Type local:CustomImage}" />
Here is where I override the OnTouchDown and OnTouchUp event handlers:
public class CustomImage : Image
{
private volatile int nrOfTouchPoints;
private volatile bool isManipulationReset;
private object mutex = new object();
static CustomImage()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomImage), new FrameworkPropertyMetadata(typeof(CustomImage)));
}
protected override void OnTouchDown(TouchEventArgs e)
{
lock (mutex)
{
nrOfTouchPoints++;
if (nrOfTouchPoints >= 2)
{
IsManipulationEnabled = true;
isManipulationReset = false;
}
}
base.OnTouchDown(e);
}
protected override void OnTouchUp(TouchEventArgs e)
{
lock (mutex)
{
if (!isManipulationReset)
{
IsManipulationEnabled = false;
isManipulationReset = true;
nrOfTouchPoints = 0;
}
}
base.OnTouchUp(e);
}
}
What I expect from this code is the following:
When using one finger to swipe horizontally or vertically across the touchscreen, the image should be scrolled accordingly;
When I use a pinch gesture on the touch screen, the image should be zoomed in/out in the center of the pinch.
Fortunately, I managed to find the perfect solution. Therefore, I am going to post the answer in the case that someone is working on a similar problem and needs some help.
What I did:
Got rid of the custom control as it was not necessary;
Create a field which counts the number of the touch points;
Implemented the TouchDown event handler, which increases the number of touch points by 1 (this method is called each time there is a touch down gesture on the device);
Implemented the TouchUp event handler, which decreases the number of touch points by 1 (this method is called each time there is a touch up gesture on the device);
In the Image_ManipulationDelta event handler, I check the number of touch points:
if the number of touch points < 2, then the translation value is added to the current offset of the scrollbars, thus achieving scrolling;
otherwise, the center of the pinch is calculated and a scale gesture is applied.
Here is the full XAML:
<Grid
x:Name="GridParent">
<ScrollViewer
x:Name="ScrollViewerParent"
HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto"
PanningMode="Both">
<Image
x:Name="MainImage"
Source="{Binding Source={x:Static local:Constants.ImagePath}}"
IsManipulationEnabled="True"
TouchDown="MainImage_TouchDown"
TouchUp="MainImage_TouchUp"
ManipulationDelta="Image_ManipulationDelta"
ManipulationStarting="Image_ManipulationStarting"/>
</ScrollViewer>
</Grid>
Here is the entire code discussed above:
public partial class MainWindow : Window
{
private volatile int nrOfTouchPoints;
private object mutex = new object();
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
private void Image_ManipulationStarting(object sender, ManipulationStartingEventArgs e)
{
e.ManipulationContainer = ScrollViewerParent;
e.Handled = true;
}
private void Image_ManipulationDelta(object sender, ManipulationDeltaEventArgs e)
{
int nrOfPoints = 0;
lock (mutex)
{
nrOfPoints = nrOfTouchPoints;
}
if (nrOfPoints >= 2)
{
DataLogger.LogActionDescription($"Executed {nameof(Image_ManipulationDelta)}");
var matrix = MainImage.LayoutTransform.Value;
Point? centerOfPinch = (e.ManipulationContainer as FrameworkElement)?.TranslatePoint(e.ManipulationOrigin, ScrollViewerParent);
if (centerOfPinch == null)
{
return;
}
var deltaManipulation = e.DeltaManipulation;
matrix.ScaleAt(deltaManipulation.Scale.X, deltaManipulation.Scale.Y, centerOfPinch.Value.X, centerOfPinch.Value.Y);
MainImage.LayoutTransform = new MatrixTransform(matrix);
Point? originOfManipulation = (e.ManipulationContainer as FrameworkElement)?.TranslatePoint(e.ManipulationOrigin, MainImage);
double scrollViewerOffsetX = ScrollViewerParent.HorizontalOffset;
double scrollViewerOffsetY = ScrollViewerParent.VerticalOffset;
double pointMovedOnXOffset = originOfManipulation.Value.X - originOfManipulation.Value.X * deltaManipulation.Scale.X;
double pointMovedOnYOffset = originOfManipulation.Value.Y - originOfManipulation.Value.Y * deltaManipulation.Scale.Y;
double multiplicatorX = ScrollViewerParent.ExtentWidth / MainImage.ActualWidth;
double multiplicatorY = ScrollViewerParent.ExtentHeight / MainImage.ActualHeight;
ScrollViewerParent.ScrollToHorizontalOffset(scrollViewerOffsetX - pointMovedOnXOffset * multiplicatorX);
ScrollViewerParent.ScrollToVerticalOffset(scrollViewerOffsetY - pointMovedOnYOffset * multiplicatorY);
e.Handled = true;
}
else
{
ScrollViewerParent.ScrollToHorizontalOffset(ScrollViewerParent.HorizontalOffset - e.DeltaManipulation.Translation.X);
ScrollViewerParent.ScrollToVerticalOffset(ScrollViewerParent.VerticalOffset - e.DeltaManipulation.Translation.Y);
}
}
private void MainImage_TouchDown(object sender, TouchEventArgs e)
{
lock (mutex)
{
nrOfTouchPoints++;
}
}
private void MainImage_TouchUp(object sender, TouchEventArgs e)
{
lock (mutex)
{
nrOfTouchPoints--;
}
}
}
}
I'm attempting to emulate the behavior of Windows 10's Virtual Touchpad, ie when a user touches a control inside the app and moves their finger around, the system cursor will mirror their movement. However, I notice that there seems to be some conflict between the system processing touch and mouse inputs simultaneously, where the touch input wants to hide the cursor, and the mouse input wants to show the cursor.
I started by building a boilerplate UWP App in VS-2019, here's my MainPage.xaml, where the only thing I'm changing is giving my Grid the Name="Touchpad" property so I can track pointer events within it:
<Page
x:Class="Playground.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Playground"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid Background="Black" Name="Touchpad">
</Grid>
</Page>
I'm using Windows.UI.Input.Preview.Injection to move the mouse cursor. Because this is a Restricted Capability I made changes to my project as defined here: https://learn.microsoft.com/en-us/uwp/api/windows.ui.input.preview.injection#remarks
public sealed partial class MainPage : Page
{
private Point lastPosition;
private InputInjector inputInjector = InputInjector.TryCreate();
public MainPage()
{
this.InitializeComponent();
ApplicationView.PreferredLaunchViewSize = new Size(480, 480);
ApplicationView.PreferredLaunchWindowingMode = ApplicationViewWindowingMode.PreferredLaunchViewSize;
Touchpad.PointerPressed += new PointerEventHandler(Touchpad_PointerPressed);
Touchpad.PointerReleased += new PointerEventHandler(Touchpad_PointerReleased);
Touchpad.PointerMoved += new PointerEventHandler(Touchpad_PointerMoved);
}
private void Touchpad_PointerMoved(object sender, PointerRoutedEventArgs e)
{
e.Handled = true;
PointerPoint pointer = e.GetCurrentPoint(Touchpad);
Point currentPosition = pointer.Position;
if (pointer.PointerDevice.PointerDeviceType == Windows.Devices.Input.PointerDeviceType.Touch &&
lastPosition != currentPosition &&
pointer.Properties.IsPrimary == true)
{
Point delta = new Point(currentPosition.X - lastPosition.X, currentPosition.Y - lastPosition.Y);
InjectedInputMouseInfo mouseInfo = new InjectedInputMouseInfo();
mouseInfo.MouseOptions = InjectedInputMouseOptions.Move;
mouseInfo.DeltaX = (int)delta.X;
mouseInfo.DeltaY = (int)delta.Y;
if (inputInjector != null)
{
inputInjector.InjectMouseInput(new[] { mouseInfo });
}
lastPosition = currentPosition;
}
}
private void Touchpad_PointerReleased(object sender, PointerRoutedEventArgs e)
{
e.Handled = true;
lastPosition = new Point(0,0);
}
private void Touchpad_PointerPressed(object sender, PointerRoutedEventArgs e)
{
e.Handled = true;
PointerPoint pointer = e.GetCurrentPoint(Touchpad);
if (pointer.PointerDevice.PointerDeviceType == Windows.Devices.Input.PointerDeviceType.Touch &&
pointer.Properties.IsPrimary == true)
{
lastPosition = pointer.Position;
Window.Current.CoreWindow.PointerCursor = null;
}
}
}
I noticed that while moving my finger around, there seems to be two cursors (or one that's bouncing back and forth really fast) - one in the app itself, and one in the system/desktop. I added Window.Current.CoreWindow.PointerCursor = null; which successfully hides the cursor inside the app, but the one that's being driven by the injection is blinking/flickering.
I feel like I'm missing something, as the feedback on the Virtual Touchpad in Win10 is buttery smooth and is also a UWP app, as far as I know...
so I have this Window Application that simulates mouse movement, I made a few tests and managed to "record" mouse movement and play it once recorded. Now I wanna make a Viewlist with 2 columns ("X" and "Y) containing the coords of the route I want my mouse to follow. Coordinates can be added throught "ycoord" and "xcoord" textlabels and can be added with "button1" button to the viewlist. There's also a "button2" that works as a "toggle" button to turn it on and off. So, I want is that, every time I press left click form mouse (LButton) the movement runs as long as I hold this button, and, if I hold it again it starts over again. I already managed the textlabels to place content into the viewlist, but I don't know how to read the viewlist and convert them into mouse movement coords.
Thanks in advance for the help!
You have to save the X,Y coords in a List, so you can create a list of points, then you have to iterate it and use it with:
Look at the object Cursor here
Also you have the code:
private void MoveCursor()
{
// Set the Current cursor, move the cursor's Position,
// and set its clipping rectangle to the form.
this.Cursor = new Cursor(Cursor.Current.Handle);
Cursor.Position = new Point(Cursor.Position.X - 50, Cursor.Position.Y - 50);
Cursor.Clip = new Rectangle(this.Location, this.Size);
}
Try this:
public partial class Form1 : Form
{
private List<Point> _points = null;
public Form1()
{
InitializeComponent();
_points = new List<Point>();
this.MouseMove += Form1_MouseMove;
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
//Save the point
_points.Add(new Point(e.X, e.Y));
}
private void PerformRecord()
{
foreach (var point in _points)
{
this.Cursor = new Cursor(Cursor.Current.Handle);
Cursor.Position = new Point(point.X, point.Y);
}
}
private void button1_Click(object sender, EventArgs e)
{
PerformRecord();
}
}
I am trying to capture a signature in windows phone 7.1.
I can draw on the screen yet I can not limit the drawing area to the InkPresenter control except by adding some handling in the mousemove event.
How can I limit the drawing area using XAML or is this not possible?
XAML Code
<InkPresenter Name="inkTest" Background="White" MinHeight="180" MinWidth="250" />
Code Behind
private Stroke _currentStroke;
private void inkTest_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
_currentStroke = null;
}
private void inkTest_MouseMove(object sender, MouseEventArgs e)
{
if (_currentStroke == null) return;
//HACK: want to set this in XAML
var position = e.GetPosition(inkTest);
if (position.X <= inkTest.ActualWidth &&
position.Y <= inkTest.ActualHeight)
_currentStroke.StylusPoints.Add(GetStylusPoint(position));
}
private void inkTest_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
inkTest.CaptureMouse();
_currentStroke = new Stroke();
_currentStroke.StylusPoints.Add(GetStylusPoint(e.GetPosition(inkTest)));
_currentStroke.DrawingAttributes.Color = Colors.Blue;
inkTest.Strokes.Add(_currentStroke);
}
private StylusPoint GetStylusPoint(Point position)
{
return new StylusPoint(position.X, position.Y);
}
Untested, but try clipping:
<InkPresenter Name="inkTest" Background="White" MinHeight="180" MinWidth="250">
<InkPresenter.Clip>
<RectangleGeometry Rect="0,0,180,250"/>
</InkPresenter.Clip>
</InkPresenter>
Change the boundaries of the RectangleGeometry to what you want (or change the RectangleGeometry element itself if you need a different shape).