I am using the below code to get the current mouse position in a WPF application.
System.Drawing.Point _point = System.Windows.Forms.Control.MousePosition;
This works good. But when the user has a 125% display settings in the machine (Windows 7), the mouse position is wrong. Am I doing anything wrong?
See if anything in this Blog or this Blog helps and since you are using Wpf try using Mouse.GetPosition as in this modified MSDN example:
// displayArea is the main window and txtBoxMousePosition is
// a TextBox used to display the position of the mouse pointer.
private void Window_MouseMove(object sender, MouseEventArgs e)
{
Point position = Mouse.GetPosition(this);
txtBoxMousePosition.Text = "X: " + position.X + "\n" + "Y: " + position.Y;
}
Related
I have a question regarding WPF.
I have a Canvas that serves as a visual editor! I have a few 'nodes' positioned in the Canvas using 'X' and 'Y' properties (Canvas.Left and Canvas.Top). Now, I need this Canvas to let the user Zoom (in & out) and Pan around, as he want's to.
I implemented kind of a hack to emulate that behavior. This is the Code that let's the user 'pan' around in the Canvas:
///In file MainWindow.xaml.cs
private void ZoomPanCanvas_MouseMove(object sender, MouseEventArgs e) {
if (IsMouseDown) {
///Change the Cursor to Scroll
if (mNetworkUI.Cursor != Cursors.ScrollAll)
mNetworkUI.Cursor = Cursors.ScrollAll;
var currPosition = e.GetPosition(mNetworkUI);
var diff = currPosition - MouseLastPosition;
var p = new Point(diff.X, diff.Y);
mNetworkUI.ViewModel.Network.SetTransformOffset(p);
MouseLastPosition = currPosition;
}
}
///In file NetworkViewModel.cs
public void SetTransformOffset(Point newOffset) {
for (int i = 0; i < Nodes.Count; i++) {
Nodes[i].X += newOffset.X;
Nodes[i].Y += newOffset.Y;
}
}
Where the 'Nodes' are my editor-nodes displayed in the Canvas. The Zooming (with respect to the mouse position works as follows:
///File MainWindow.xaml.cs
private void ZoomPanCanvas_MouseWheel(object sender, MouseWheelEventArgs e) {
///Determine the Scaling Factor and Scale the Rule-Editor
var factor = (e.Delta > 0) ? (1.1) : (1 / 1.1);
currrentScale = factor * currrentScale;
ScaleNetwork();
///Translate the Nodes to the desired Positions
var pos = e.GetPosition(mNetworkUI);
var transform = new ScaleTransform(factor, factor, pos.X, pos.Y);
var offSet = new Point(transform.Value.OffsetX, transform.Value.OffsetY);
mNetworkUI.ViewModel.Network.SetTransformOffset(offSet);
}
///Also in MainWindow.xaml.cs
private void ScaleNetwork() {
mNetworkUI.RenderTransform = new ScaleTransform(currrentScale, currrentScale);
mNetworkUI.Width = ZoomPanCanvas.ActualWidth / currrentScale;
mNetworkUI.Height = ZoomPanCanvas.ActualHeight / currrentScale;
}
So, in the 'panning' I calculate the difference to the last mouse position and use that vector to manipulate the nodes, not the Canvas itself.
When I zoom, I determine the new zoom, set a new RenderTransform, resize the Canvas to again fill the provided space and again re-position the nodes in the Canvas.
It works very well for now. I can 'pan & zoom' around how I want, but I realized, that with many nodes present in my 'network' (connected nodes), things get quite slow.
One reason is, that on every movement of a node some events are raised resulting in a noticable delay when panning.
How is such a thing (without fixed Canvas-size and Scrollbars) possible in a performant manner? Is there a control out there that I can use? Is this possible with the Extended WPF toolkit's ZoomBox control?
Thank you!
I've written a Viewport control for this exact functionality.
I've also packaged this up on nuget
PM > Install-Package Han.Wpf.ViewportControl
It extends a ContentControl which can contain any FrameworkElement and provides constrained zoom and pan functionality. Just make sure to add Generic.xaml to your app.xaml
<Application.Resources>
<ResourceDictionary Source="pack://application:,,,/Han.Wpf.ViewportControl;component/Themes/Generic.xaml" />
</Application.Resources>
Usage:
<Grid width="1200" height="1200">
<Button />
</Grid>
The source code for the control and theme is on my gist and can be found on my github along with a demo application that loads an image into the viewport control.
Can someone point me in the right track for displaying the XY mouse position on a Canvas using a tooltip?
I would like to drag the mouse and the tooltip update the position as the user moves the mouse. I tried doing this programmatically using the mouse enter to update the tooltip but was failing to get anything to show up. Thanks
If anyone is interested in the solution...
private void Canvas_MouseMove(object sender, MouseEventArgs e)
{
tt.Placement = System.Windows.Controls.Primitives.PlacementMode.Relative;
tt.HorizontalOffset = e.GetPosition((IInputElement)sender).X + 10;
tt.VerticalOffset = e.GetPosition((IInputElement)sender).Y + 10;
tt.Content = "X-Coordinate: " + e.GetPosition((IInputElement)sender).X + "\n" + "Y-Coordinate: " + e.GetPosition((IInputElement)sender).Y;
}
I have a WPF Image inside a Border I currently am able to click and drag to pan the image. I want to prevent users from dragging the image off the screen. Perhaps a minimum of 100 pixels should be showing at any border. That is, if a user drags the image all the way to the left, most of the image will disappear, but 100 pixels will still be hanging out toward the left boundary of the border.
Here's my current code for the MouseMove and LeftClick event:
private void img_Box_MouseMove(object sender, MouseEventArgs e)
{
if (!img_Box.IsMouseCaptured) return;
var tt = (TranslateTransform)((TransformGroup)img_Box.RenderTransform).Children.First(tr => tr is TranslateTransform);
Vector v = start - e.GetPosition(img_Border);
tt.X = origin.X - v.X;
tt.Y = origin.Y - v.Y;
}
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);
start = e.GetPosition(img_Border);
origin = new System.Windows.Point(tt.X, tt.Y);
}
I think I should be able to put a pretty simple condition on that to achieve my desired result, but I can't figure out what the condition should be.
I would like to make a small silverlight app which displays one fairly large image which can be zoomed in by scrolling the mouse and then panned with the mouse.
it's similar to the function in google maps and i do not want to use deepzoom.
here is what i have at the moment. please keep in mind that this is my first silverlight app:
this app is just for me to see it's a good way to build in a website. so it's a demo app and therefor has bad variable names.
the initial image is 1800px width.
private void sc_MouseWheel(object sender, MouseWheelEventArgs e)
{
var st = (ScaleTransform)plaatje.RenderTransform;
double zoom = e.Delta > 0 ? .1 : -.1;
st.ScaleX += zoom;
st.ScaleY += zoom;
}
this works, but could use some smoothing and it's positioned top left and not centered.
the panning is like this:
found it # Pan & Zoom Image
and converted it to this below to work in silverlight
Point start;
Point origin;
bool captured = false;
private void plaatje_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
plaatje.CaptureMouse();
captured = true;
var tt = (TranslateTransform)((TransformGroup)plaatje.RenderTransform)
.Children.First(tr => tr is TranslateTransform);
start = e.GetPosition(canvasje);
origin = new Point(tt.X, tt.Y);
}
private void plaatje_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
plaatje.ReleaseMouseCapture();
captured = false;
}
private void plaatje_MouseMove(object sender, MouseEventArgs e)
{
if (!captured) return;
var tt = (TranslateTransform)((TransformGroup)plaatje.RenderTransform).Children.First(tr => tr is TranslateTransform);
double xVerschuiving = start.X - e.GetPosition(canvasje).X;
double yVerschuiving = start.Y - e.GetPosition(canvasje).Y;
tt.X = origin.X - xVerschuiving;
tt.Y = origin.Y - yVerschuiving;
}
so the scaling isn't smooth and the panning isn't working, because when i click it, the image disappears.
first thing I noticed was this:
var st = (ScaleTransform)plaatje.RenderTransform;
and
(TransformGroup)plaatje.RenderTransform
. So in one handler, you're casting "plaatje.RenderTransform" to ScaleTransform, in the other you're casting to TransformGroup?
This is probably causing an exception, making your image disappear.
For the zooming part, you might want to try setting the RenderTransformOrigin of the object you want to scale ("plaatje"?) to "0.5,0.5", meaning the center of the UI element. So the image will be scaled around its center point instead of its top left corner.
Cheers, Alex
I have a Canvas inside a ScrollViewer. I want to have the user to be able to grab the canvas and move it around, with the thumbs on the scrollbars updating appropriately.
My initial implementation calculates the offset on each mouse move, and updates the scrollbars:
// Calculate the new drag distance
Point newOffsetPos = e.GetPosition(MapCanvas);
System.Diagnostics.Debug.WriteLine(" newOffsetPos : " + newOffsetPos.X + " " + newOffsetPos.Y);
double deltaX = newOffsetPos.X - _offsetPosition.X ;
double deltaY = newOffsetPos.Y - _offsetPosition.Y ;
System.Diagnostics.Debug.WriteLine(" delta X / Y : " + deltaX + " " + deltaY);
System.Diagnostics.Debug.WriteLine(" sv offsets X / Y : " + _scrollViewer.HorizontalOffset + " " + _scrollViewer.VerticalOffset);
_scrollViewer.ScrollToHorizontalOffset(_scrollViewer.HorizontalOffset - deltaX);
_scrollViewer.ScrollToVerticalOffset(_scrollViewer.VerticalOffset - deltaY);
_offsetPosition = newOffsetPos;
While this works, it is not very smooth.
Is there a better way to do this? If Transforms are used, will the scrollbars update automagically when the Canvas is moved?
Thanks for any tips...
Actually this sort of problem is really a matter of using the right pattern to track the mouse. I've seen this issue in variety of cases not just in Silverlight.
The best pattern is to trap the original locations of both mouse and subject, then recalculate the new offset from the fixed original values. That way the mouse stays planted solid at a single point on the image being panned. Here is my simple Repro:-
Start with a fresh Silverlight Application in visual studio. Modify MainPage.Xaml thus:-
<UserControl x:Class="SilverlightApplication1.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid x:Name="LayoutRoot" Background="White">
<ScrollViewer x:Name="Scroller" HorizontalScrollBarVisibility="Auto">
<Image x:Name="Map" Source="test.jpg" Width="1600" Height="1200" />
</ScrollViewer>
</Grid>
</UserControl>
Add the following code to the MainPage.xaml.cs file:-
public MainPage()
{
InitializeComponent();
Map.MouseLeftButtonDown += new MouseButtonEventHandler(Map_MouseLeftButtonDown);
}
void Map_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Point mapOrigin = new Point(Scroller.HorizontalOffset, Scroller.VerticalOffset);
Point mouseOrigin = e.GetPosition(Application.Current.RootVisual);
MouseEventHandler moveHandler = null;
MouseButtonEventHandler upHandler = null;
moveHandler = (s, args) =>
{
Point mouseNew = args.GetPosition(Application.Current.RootVisual);
Scroller.ScrollToHorizontalOffset(mapOrigin.X - (mouseNew.X - mouseOrigin.X));
Scroller.ScrollToVerticalOffset(mapOrigin.Y - (mouseNew.Y - mouseOrigin.Y));
};
upHandler = (s, args) =>
{
Scroller.MouseMove -= moveHandler;
Scroller.MouseLeftButtonUp -= upHandler;
};
Scroller.MouseMove += moveHandler;
Scroller.MouseLeftButtonUp += upHandler;
}
}
Give it a reasonably large test.jpg (doesn't need to be 1600x1200 Image will scale it).
You'll note that when dragging the mouse remains exactly over a fixed point in the image until you hit a boundary. Move the mouse as fast as you like it always tracks, this is because it doesn't depend on deltas being accurate and up-to-date. The only variable is the current mouse position, the other values remain fixed as they were at mouse down.
You could try this (or at least take a peek at how it's implemented).