My requirement is to do pinch zooming in WPF platform. For that, i have created simple POC sample according to my requirement and it can be reproduced. Please find the snippets below
XAML:
<Grid>
<Image x:Name="JPGImage"
Source="TestImage.jpg"
IsManipulationEnabled="True"
ManipulationDelta="Image_ManipulationDelta"
ManipulationStarted="JPGImage_ManipulationStarted"
ManipulationCompleted="JPGImage_ManipulationCompleted"/>
</Grid>
C#:
private void Image_ManipulationDelta(object sender, ManipulationDeltaEventArgs e)
{
System.Diagnostics.Debug.WriteLine(e.DeltaManipulation.Translation.X + " " + e.DeltaManipulation.Translation.Y);
if (e.Manipulators.Count() > 1)
{
if (scale.ScaleX > 1.6 || scale.ScaleX < 0.5)
{
scale.ScaleX = 1;
scale.ScaleY = 1;
translation.X = 0;
translation.Y = 0;
this.JPGImage.RenderTransform = transformGroup;
}
else
{
if (isScale)
{
scale.ScaleX = previousCumlativeScale * e.DeltaManipulation.Scale.X;
scale.ScaleY = previousCumlativeScale * e.DeltaManipulation.Scale.Y;
var x = (e.ManipulationOrigin.X - previousXPosition) * (scale.ScaleX - 1);
var y = (e.ManipulationOrigin.Y - previousYPosition) * (scale.ScaleY - 1);
translation.X += x;
translation.Y += y;
}
else
{
scale.ScaleX *= e.DeltaManipulation.Scale.X;
scale.ScaleY *= e.DeltaManipulation.Scale.Y;
translation.X += e.DeltaManipulation.Translation.X;
translation.Y += e.DeltaManipulation.Translation.Y;
}
scale.CenterX = e.ManipulationOrigin.X;
scale.CenterY = e.ManipulationOrigin.Y;
this.JPGImage.RenderTransform = transformGroup;
}
previousCumlativeScale = scale.ScaleX;
previousTranslateX = translation.X;
previousTranslateY = translation.Y;
previousXPosition = scale.CenterX;
previousYPosition = scale.CenterY;
}
else
{
translation.X += e.DeltaManipulation.Translation.X;
translation.Y += e.DeltaManipulation.Translation.Y;
}
}
My problems are
If i try to Zoomout the image using pinch, then it get disappeared when its scale value becomes less than 1
Zooming is also not smooth
Also i have reset the zoom after certain level of scale (mentioned below) then the image is not get reset properly.
C#
if (scale.ScaleX > 1.6 || scale.ScaleX < 0.5)
{
scale.ScaleX = 1;
scale.ScaleY = 1;
translation.X = 0;
translation.Y = 0;
this.JPGImage.RenderTransform = transformGroup;
}
Please get the sample from the below link
WPFZooming
I have found the below issue only related to manipulation events in github
https://github.com/Microsoft/dotnet/issues/575
Could you please help me to resolve the problems?.
Thanks in advance
Vignesh.
Not sure why exactly your code seems so complicated. The following also works, where the important point is to handle the ManipulationDelta on the parent element of the Image element:
<Canvas IsManipulationEnabled="True"
ManipulationDelta="OnManipulationDelta">
<Image Source="...">
<Image.RenderTransform>
<MatrixTransform x:Name="imageTransform"/>
</Image.RenderTransform>
</Image>
</Canvas>
with this event handler:
private void OnManipulationDelta(object sender, ManipulationDeltaEventArgs e)
{
var origin = e.ManipulationOrigin;
var delta = e.DeltaManipulation;
var scale = (delta.Scale.X + delta.Scale.Y) / 2; // mean value
var matrix = imageTransform.Matrix; // current transform matrix
matrix.ScaleAt(scale, scale, origin.X, origin.Y);
matrix.Translate(delta.Translation.X, delta.Translation.Y);
imageTransform.Matrix = matrix; // new transform matrix
}
Related
I display the images on canvas and I want to be able to move all the images. Currently I am only able to move the last image which I brought on canvas. I also want to be able to zoom these images. Please help me in this.
private TranslateTransform dragTranslation;
// Constructor
void Drag_ManipulationDelta(object sender,ManipulationDeltaRoutedEventArgs e)
{
// Move the rectangle.
dragTranslation.X += e.Delta.Translation.X;
dragTranslation.Y += e.Delta.Translation.Y;
}
private void Stickers1_SelectionChanged(object sender,SelectionChangedEventArgs e)
{
var selecteditem = e.AddedItems[0] as StickersImageListModel;
Stickers1.Visibility = Visibility.Collapsed;
Image imageitem = new Image();
BitmapImage image = new BitmapImage(new System.Uri(selecteditem.Imageurl, UriKind.Absolute));
imageitem.Source = image;
my_canvas.Children.Add(imageitem);
imageitem.AllowDrop = true;
imageitem.ManipulationMode = ManipulationModes.All;
imageitem.ManipulationDelta += Drag_ManipulationDelta;
dragTranslation = new TranslateTransform();
imageitem.RenderTransform = this.dragTranslation;
var st = (ScaleTransform)imageitem.RenderTransform;
double zoom = e.Delta > 0 ? .2 : -.2;
st.ScaleX += zoom;
st.ScaleY += zoom;
my_canvas.Visibility = Visibility.Visible;
}
You dont have to use dragTranslation property
void Drag_ManipulationDelta(object sender,ManipulationDeltaRoutedEventArgs e)
{
// Move the rectangle.
Image img = sender as Image;
CompositeTransform ct = img.RenderTransform as CompositeTransform;
ct.ScaleX *= e.Delta.Scale;
ct.ScaleY *= e.Delta.Scale;
if (ct.ScaleX < 1.0) ct.ScaleX = 1.0;
if (ct.ScaleY < 1.0) ct.ScaleY = 1.0;
if (ct.ScaleX > 4.0) ct.ScaleX = 4.0;
if (ct.ScaleY > 4.0) ct.ScaleY = 4.0;
//Checking with canvas boundary so that image wont go behind canvas
if ((ct.TranslateX + e.Delta.Translation.X) <= (mycanvas.ActualWidth - img.ActualWidth) && ct.TranslateX + e.Delta.Translation.X>=0)
ct.TranslateX += e.Delta.Translation.X;
if ((ct.TranslateY + e.Delta.Translation.Y) <= (mycanvas.ActualHeight - img.ActualHeight) && ct.TranslateY + e.Delta.Translation.Y >= 0)
ct.TranslateY += e.Delta.Translation.Y;
}
private void Stickers1_SelectionChanged(object sender,SelectionChangedEventArgs e)
{
...
//Using CompositeTransform instead of TranslateTransform
CompositeTransform ct = new CompositeTransform();
imageitem.RenderTransform = ct;
}
I have been working on a Windows 8.1 RT app where the user loads an image with Stretch=Uniform.
The image can be as small as possible and as big as possible.
The clipping happens in my user control and my user control appears when I tap/press and hold on the screen/image.
Clipping happens when when I tap and hold and move my finger/mouse around the image that is in the background.
The Problems I am facing are
Whenever the app is loaded for the first time on Emulator , and for the very first time when the tap /clicks and holding is performed , the user control appears on the top left on the screen and then it comes above my clicked/hold area. What I require is it should always appear wherever I click and hold and whenever I click and hold. On releasing the finger/mouse , it should collapse.
I am using center transform. I want the region(the pixel) where my mouse is currently to be displayed exactly in the center of the user control , If i am loading a small resolution image 480*800 or even smaller , the region of my mouse is not coming into the center.
To be more clearer, Imagine I am tapping and holding on the CORNEA of the human eye.The cornea should be displayed in the center of the user control and area above and below should cover the rest of the part.
I don't want my control to go outside my image boundary, if my mouse is at the last pixel of the image , some part of image and some part of background should be displayed.
I need to rotate the control as shown in the video
Find the complete code below.
MainPage.XAML
<Page
x:Class="App78.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App78"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid
x:Name="LayoutGrid"
Margin="0,0"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
Holding="LayoutGrid_Holding"
PointerMoved="LayoutGrid_OnPointerMoved"
PointerWheelChanged="LayoutGrid_OnPointerWheelChanged"
PointerPressed="LayoutGrid_OnPointerPressed"
PointerReleased="LayoutGrid_OnPointerReleased">
<Image
x:Name="BigImage"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Stretch="Uniform"
Source="http://blog.al.com/space-news/2009/04/iss015e22574.jpg" />
<local:Magnifier VerticalAlignment="Top" HorizontalAlignment="Left" x:Name="MagnifierTip" Visibility="Collapsed" />
</Grid>
</Page>
MAINPAGE.XAML.CS
using System;
using System.Diagnostics;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Imaging;
namespace App78
{
public sealed partial class MainPage : Page
{
private double zoomScale = 2;
private double pointerX = 0;
private double pointerY = 0;
private const double MinZoomScale = .25;
private const double MaxZoomScale = 32;
public MainPage()
{
this.InitializeComponent();
var bi = (BitmapImage)BigImage.Source;
bi.ImageOpened += bi_ImageOpened;
this.SizeChanged += MainPage_SizeChanged;
}
void MainPage_SizeChanged(object sender, Windows.UI.Xaml.SizeChangedEventArgs e)
{
this.UpdateImageLayout();
}
void bi_ImageOpened(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
this.UpdateImageLayout();
}
private void UpdateImageLayout()
{
var bi = (BitmapImage)BigImage.Source;
if (bi.PixelWidth < this.LayoutGrid.ActualWidth &&
bi.PixelHeight < this.LayoutGrid.ActualHeight)
{
this.BigImage.Stretch = Stretch.None;
}
else
{
this.BigImage.Stretch = Stretch.Uniform;
}
this.UpdateMagnifier();
}
private void LayoutGrid_OnPointerMoved(object sender, PointerRoutedEventArgs e)
{
//DV: If pointer is not in contact we can ignore it
if (!e.Pointer.IsInContact) { return; }
var point = e.GetCurrentPoint(this.LayoutGrid);
this.pointerX = point.Position.X;
this.pointerY = point.Position.Y;
this.UpdateMagnifier();
}
private void UpdateMagnifier()
{
var bi = (BitmapImage)BigImage.Source;
double offsetX = 0;
double offsetY = 0;
double imageScale = 1;
var imageRatio = (double)bi.PixelWidth / bi.PixelHeight;
var gridRatio = this.LayoutGrid.ActualWidth / this.LayoutGrid.ActualHeight;
if (bi.PixelWidth < this.LayoutGrid.ActualWidth &&
bi.PixelHeight < this.LayoutGrid.ActualHeight)
{
offsetX = 0.5 * (this.LayoutGrid.ActualWidth - bi.PixelWidth);
offsetY = 0.5 * (this.LayoutGrid.ActualHeight - bi.PixelHeight);
//imageScale = 1; - remains
}
else if (imageRatio < gridRatio)
{
offsetX = 0.5 * (this.LayoutGrid.ActualWidth - imageRatio * this.LayoutGrid.ActualHeight);
offsetY = 0;
imageScale = BigImage.ActualHeight / bi.PixelHeight;
}
else
{
offsetX = 0;
offsetY = 0.5 * (this.LayoutGrid.ActualHeight - this.LayoutGrid.ActualWidth / imageRatio);
imageScale = BigImage.ActualWidth / bi.PixelWidth;
}
//DV: This is probably not need anymore
//MagnifierTip.MagnifierTransform.X = this.pointerX;
//MagnifierTip.MagnifierTransform.Y = this.pointerY;
MagnifierTip.PositionTransform.X = (-this.pointerX + offsetX) / imageScale;
MagnifierTip.PositionTransform.Y = (-this.pointerY + offsetY) / imageScale;
//DV: I haven't tested the Scaling/Zoom
MagnifierTip.ZoomTransform.ScaleX = imageScale * zoomScale;
MagnifierTip.ZoomTransform.ScaleY = imageScale * zoomScale;
MagnifierTip.CenterTransform.X = MagnifierTip.MagnifierEllipse.ActualWidth / 2 - MagnifierTip.MagnifierEllipse.StrokeThickness / 2;
MagnifierTip.CenterTransform.Y = MagnifierTip.MagnifierEllipse.ActualHeight / 2 - MagnifierTip.MagnifierEllipse.StrokeThickness / 2;
//DV: I added a GlobalGrid Transform which translates every children
MagnifierTip.MagnifierTransformGrid.X = this.pointerX - (MagnifierTip.ActualWidth / 2);
MagnifierTip.MagnifierTransformGrid.Y = this.pointerY - (MagnifierTip.ActualHeight); ;
}
private void LayoutGrid_OnPointerWheelChanged(object sender, PointerRoutedEventArgs e)
{
if (e.GetCurrentPoint(this.LayoutGrid).Properties.MouseWheelDelta > 0)
{
zoomScale = Math.Max(MinZoomScale, Math.Min(MaxZoomScale, zoomScale * 1.2));
}
else
{
zoomScale = Math.Max(MinZoomScale, Math.Min(MaxZoomScale, zoomScale / 1.2));
}
this.UpdateMagnifier();
}
//DV: Holding usually only works with touch https://msdn.microsoft.com/en-us/library/windows/apps/windows.ui.xaml.uielement.holding.aspx?f=255&MSPPError=-2147217396
private void LayoutGrid_Holding(object sender, HoldingRoutedEventArgs e)
{
//
}
//DV: pointer pressed supports both mouse and touch but fires immeadiatley. You'll have to figure out a delay strategy or using holding for touch and right click for mouse
private void LayoutGrid_OnPointerPressed(object sender, PointerRoutedEventArgs e)
{
MagnifierTip.Visibility = Windows.UI.Xaml.Visibility.Visible;
}
//DV: pointer released supports both mouse and touch.
private void LayoutGrid_OnPointerReleased(object sender, PointerRoutedEventArgs e)
{
MagnifierTip.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
}
}
}
Magnifier.XAML
<UserControl
x:Class="App78.Magnifier"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App78"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Height="230"
Width="170">
<Grid Height="230" Width="170">
<!-- DV: This is the global transform I added -->
<Grid.RenderTransform>
<TransformGroup>
<TranslateTransform x:Name="MagnifierTransformGrid" x:FieldModifier="public"/>
</TransformGroup>
</Grid.RenderTransform>
<Ellipse Opacity="1" Visibility="Visible" Fill="{ThemeResource ApplicationPageBackgroundThemeBrush}" HorizontalAlignment="Center" VerticalAlignment="Top" IsHitTestVisible="False" Width="135" Height="128" StrokeThickness="3" Margin="0,17,0,0" />
<Ellipse x:Name="MagnifierEllipse" x:FieldModifier="public" Opacity="1" Visibility="Visible" HorizontalAlignment="Left" VerticalAlignment="Top" IsHitTestVisible="False" Width="150" Height="150" Stroke="White" StrokeThickness="3" Margin="11,8,0,0" >
<Ellipse.Fill>
<ImageBrush
ImageSource="http://blog.al.com/space-news/2009/04/iss015e22574.jpg"
Stretch="None"
AlignmentX="Left"
AlignmentY="Top">
<ImageBrush.Transform>
<TransformGroup>
<TranslateTransform x:FieldModifier="public"
x:Name="CenterTransform"/>
<TranslateTransform x:FieldModifier="public"
x:Name="PositionTransform"/>
<ScaleTransform x:FieldModifier="public"
x:Name="ZoomTransform"/>
</TransformGroup>
</ImageBrush.Transform>
</ImageBrush>
</Ellipse.Fill>
</Ellipse>
<Path Data="M25.533,0C15.457,0,7.262,8.199,7.262,18.271c0,9.461,13.676,19.698,17.63,32.338 c0.085,0.273,0.34,0.459,0.626,0.457c0.287-0.004,0.538-0.192,0.619-0.467c3.836-12.951,17.666-22.856,17.667-32.33 C43.803,8.199,35.607,0,25.533,0z M25.533,32.131c-7.9,0-14.328-6.429-14.328-14.328c0-7.9,6.428-14.328,14.328-14.328 c7.898,0,14.327,6.428,14.327,14.328C39.86,25.702,33.431,32.131,25.533,32.131z"
Fill="#FFF4F4F5"
Stretch="Fill"
Stroke="Black"
UseLayoutRounding="False"
Height="227"
Width="171" ></Path>
</Grid>
</UserControl>
For ease , the project can be downloaded from here. For better understanding , I would like you to see this video. This is what exactly I need to implement.
I got it working with some changes and trigonometry here.
MainPage.xaml
<Page
x:Class="MagnifierApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MagnifierApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid
x:Name="LayoutGrid"
Margin="0,0"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
PointerMoved="LayoutGrid_OnPointerMoved"
PointerWheelChanged="LayoutGrid_OnPointerWheelChanged"
PointerPressed="LayoutGrid_OnPointerPressed"
PointerReleased="LayoutGrid_OnPointerReleased">
<Image
x:Name="BigImage"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Stretch="Uniform"
Source="http://blog.al.com/space-news/2009/04/iss015e22574.jpg" />
<local:Magnifier
x:Name="MagnifierTip"
VerticalAlignment="Top"
HorizontalAlignment="Left"
Visibility="Collapsed" />
</Grid>
</Page>
MainPage.xaml.cs
using System;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Imaging;
namespace MagnifierApp
{
public sealed partial class MainPage : Page
{
private double zoomScale = 2;
private double pointerX = 0;
private double pointerY = 0;
private const double MinZoomScale = .25;
private const double MaxZoomScale = 32;
private bool isFirst = true;
public MainPage()
{
this.InitializeComponent();
var bi = (BitmapImage)BigImage.Source;
bi.ImageOpened += bi_ImageOpened;
this.SizeChanged += MainPage_SizeChanged;
}
void MainPage_SizeChanged(object sender, Windows.UI.Xaml.SizeChangedEventArgs e)
{
this.UpdateImageLayout();
}
void bi_ImageOpened(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
this.UpdateImageLayout();
}
private void UpdateImageLayout()
{
var bi = (BitmapImage)BigImage.Source;
if (bi.PixelWidth < this.LayoutGrid.ActualWidth &&
bi.PixelHeight < this.LayoutGrid.ActualHeight)
{
this.BigImage.Stretch = Stretch.None;
}
else
{
this.BigImage.Stretch = Stretch.Uniform;
}
this.UpdateMagnifier();
}
private void LayoutGrid_OnPointerMoved(object sender, PointerRoutedEventArgs e)
{
if (!e.Pointer.IsInContact)
{
return;
}
var point = e.GetCurrentPoint(this.LayoutGrid);
this.pointerX = point.Position.X;
this.pointerY = point.Position.Y;
this.UpdateMagnifier();
}
private void LayoutGrid_OnPointerWheelChanged(object sender, PointerRoutedEventArgs e)
{
if (e.GetCurrentPoint(this.LayoutGrid).Properties.MouseWheelDelta > 0)
{
zoomScale = Math.Max(MinZoomScale, Math.Min(MaxZoomScale, zoomScale * 1.2));
}
else
{
zoomScale = Math.Max(MinZoomScale, Math.Min(MaxZoomScale, zoomScale / 1.2));
}
this.UpdateMagnifier();
}
private void LayoutGrid_OnPointerPressed(object sender, PointerRoutedEventArgs e)
{
if (!e.Pointer.IsInContact) { return; }
var point = e.GetCurrentPoint(this.LayoutGrid);
this.pointerX = point.Position.X;
this.pointerY = point.Position.Y;
this.UpdateMagnifier();
MagnifierTip.Visibility = Windows.UI.Xaml.Visibility.Visible;
}
private void LayoutGrid_OnPointerReleased(object sender, PointerRoutedEventArgs e)
{
MagnifierTip.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
}
private void UpdateMagnifier()
{
var bi = (BitmapImage)BigImage.Source;
double offsetX;
double offsetY;
double imageScale;
CalculateImageScaleAndOffsets(bi, out offsetX, out offsetY, out imageScale);
MagnifierTip.PositionTransform.X = (-this.pointerX + offsetX) / imageScale;
MagnifierTip.PositionTransform.Y = (-this.pointerY + offsetY) / imageScale;
MagnifierTip.ZoomTransform.ScaleX = imageScale * zoomScale;
MagnifierTip.ZoomTransform.ScaleY = imageScale * zoomScale;
MagnifierTip.CenterTransform.X = MagnifierTip.MagnifierEllipse.ActualWidth / 2 - MagnifierTip.MagnifierEllipse.StrokeThickness / 2;
MagnifierTip.CenterTransform.Y = MagnifierTip.MagnifierEllipse.ActualHeight / 2 - MagnifierTip.MagnifierEllipse.StrokeThickness / 2;
MagnifierTip.MagnifierTranslateTransform.X = this.pointerX - (MagnifierTip.ActualWidth / 2);
MagnifierTip.MagnifierTranslateTransform.Y = this.pointerY - (MagnifierTip.ActualHeight);
bool tooHigh = MagnifierTip.MagnifierTranslateTransform.Y < 0;
bool tooLeft = MagnifierTip.MagnifierTranslateTransform.X < 0;
bool tooRight = MagnifierTip.MagnifierTranslateTransform.X + MagnifierTip.ActualWidth > this.LayoutGrid.ActualWidth;
if (tooHigh || tooLeft || tooRight)
{
double angle = 0.0;
if (tooLeft && !tooHigh)
{
var dx = -MagnifierTip.MagnifierTranslateTransform.X;
var r = MagnifierTip.ActualHeight - MagnifierTip.ActualWidth / 2;
var arad = Math.Asin(dx / r);
angle = arad * 180 / Math.PI;
}
else if (tooLeft && tooHigh)
{
var dx = -MagnifierTip.MagnifierTranslateTransform.X;
var dy = -MagnifierTip.MagnifierTranslateTransform.Y;
var r = MagnifierTip.ActualHeight - MagnifierTip.ActualWidth / 2;
var arad1 = Math.Asin(dx / r);
var arad2 = Math.Acos((r - dy) / r);
var arad = Math.Max(arad1, arad2);
angle = arad * 180 / Math.PI;
}
else if (tooHigh && !tooRight)
{
var dy = -MagnifierTip.MagnifierTranslateTransform.Y;
var r = MagnifierTip.ActualHeight - MagnifierTip.ActualWidth / 2;
var arad = Math.Acos((r - dy) / r);
if (MagnifierTip.MagnifierTranslateTransform.X + Math.Sin(arad) * r + MagnifierTip.ActualWidth > this.LayoutGrid.ActualWidth)
{
arad = -arad;
}
angle = arad * 180 / Math.PI;
}
else if (tooHigh)
{
var dy = -MagnifierTip.MagnifierTranslateTransform.Y;
var dx = MagnifierTip.MagnifierTranslateTransform.X + MagnifierTip.ActualWidth - this.LayoutGrid.ActualWidth;
var r = MagnifierTip.ActualHeight - MagnifierTip.ActualWidth / 2;
var arad1 = -Math.Acos((r - dy) / r);
var arad2 = -Math.Asin(dx / r);
var arad = Math.Min(arad1, arad2);
angle = arad * 180 / Math.PI;
}
else //if (tooRight)
{
var dx = MagnifierTip.MagnifierTranslateTransform.X + MagnifierTip.ActualWidth - this.LayoutGrid.ActualWidth;
var r = MagnifierTip.ActualHeight - MagnifierTip.ActualWidth / 2;
var arad = -Math.Asin(dx / r);
angle = arad * 180 / Math.PI;
}
MagnifierTip.RotateTransform.Angle = angle;
MagnifierTip.LensRotateTransform.Angle = -angle;
}
else
{
MagnifierTip.RotateTransform.Angle = 0;
MagnifierTip.LensRotateTransform.Angle = 0;
}
}
private void CalculateImageScaleAndOffsets(BitmapImage bi, out double offsetX, out double offsetY, out double imageScale)
{
var imageRatio = (double)bi.PixelWidth / bi.PixelHeight;
var gridRatio = this.LayoutGrid.ActualWidth / this.LayoutGrid.ActualHeight;
if (bi.PixelWidth < this.LayoutGrid.ActualWidth &&
bi.PixelHeight < this.LayoutGrid.ActualHeight)
{
offsetX = 0.5 * (this.LayoutGrid.ActualWidth - bi.PixelWidth);
offsetY = 0.5 * (this.LayoutGrid.ActualHeight - bi.PixelHeight);
imageScale = 1;
}
else if (imageRatio < gridRatio)
{
offsetX = 0.5 * (this.LayoutGrid.ActualWidth - imageRatio * this.LayoutGrid.ActualHeight);
offsetY = 0;
imageScale = BigImage.ActualHeight / bi.PixelHeight;
}
else
{
offsetX = 0;
offsetY = 0.5 * (this.LayoutGrid.ActualHeight - this.LayoutGrid.ActualWidth / imageRatio);
imageScale = BigImage.ActualWidth / bi.PixelWidth;
}
}
}
}
Magnifier.xaml
<UserControl
x:Class="MagnifierApp.Magnifier"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MagnifierApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Height="227"
Width="170">
<Grid
Height="227"
Width="170"
RenderTransformOrigin="0.5,1">
<Grid.RenderTransform>
<TransformGroup>
<RotateTransform
x:FieldModifier="public"
x:Name="RotateTransform"
Angle="0" />
<TranslateTransform
x:Name="MagnifierTranslateTransform"
x:FieldModifier="public" />
</TransformGroup>
</Grid.RenderTransform>
<Ellipse
HorizontalAlignment="Left"
VerticalAlignment="Top"
IsHitTestVisible="False"
Width="152"
Height="152"
Fill="{ThemeResource ApplicationPageBackgroundThemeBrush}"
Margin="9,10,0,0"
RenderTransformOrigin="0.5,0.5"
StrokeThickness="0">
</Ellipse>
<Ellipse
x:Name="MagnifierEllipse"
x:FieldModifier="public"
HorizontalAlignment="Left"
VerticalAlignment="Top"
IsHitTestVisible="False"
Width="152"
Height="152"
Stroke="White"
Margin="9,10,0,0"
RenderTransformOrigin="0.5,0.5"
StrokeThickness="0">
<Ellipse.RenderTransform>
<RotateTransform
x:FieldModifier="public"
x:Name="LensRotateTransform"
Angle="0" />
</Ellipse.RenderTransform>
<Ellipse.Fill>
<ImageBrush
ImageSource="http://blog.al.com/space-news/2009/04/iss015e22574.jpg"
Stretch="None"
AlignmentX="Left"
AlignmentY="Top">
<ImageBrush.Transform>
<TransformGroup>
<TranslateTransform
x:FieldModifier="public"
x:Name="PositionTransform" />
<ScaleTransform
x:FieldModifier="public"
x:Name="ZoomTransform" />
<TranslateTransform
x:FieldModifier="public"
x:Name="CenterTransform" />
</TransformGroup>
</ImageBrush.Transform>
</ImageBrush>
</Ellipse.Fill>
</Ellipse>
<Path
Data="M85,11 C43.8548,11 10.5,44.3548 10.5,85.5 C10.5,126.645 43.8548,160 85,160 C126.145,160 159.5,126.645 159.5,85.5 C159.5,44.3548 126.145,11 85,11 z M85,0.5 C131.668,0.5 169.5,38.3319 169.5,85 C169.5,103.959 163.256,121.459 152.713,135.558 L151.895,136.625 L152,136.625 L84.2999,226 L18,136.625 L18.1054,136.625 L17.2872,135.558 C6.74375,121.459 0.5,103.959 0.5,85 C0.5,38.3319 38.3319,0.5 85,0.5 z"
Margin="0,0.5,0,0"
Stretch="Fill"
Stroke="Black"
UseLayoutRounding="False"
StrokeThickness="0"
Fill="White" />
</Grid>
</UserControl>
The fix for 1 here is to Update the magnifier even when within
LayoutGrid_OnPointerPressed
So the updated method looks like
private void LayoutGrid_OnPointerPressed(object sender, PointerRoutedEventArgs e)
{
MagnifierTip.Visibility = Windows.UI.Xaml.Visibility.Visible;
var point = e.GetCurrentPoint(this.LayoutGrid);
this.pointerX = point.Position.X;
this.pointerY = point.Position.Y;
this.UpdateMagnifier();
}
This gives you the desired behavior that you are talking about.
For resolving the bounds issue
calculate the pointer values above with respect to BitImage instead of LayoutGrid.
if (this.pointerX < 0.0 || this.pointerY < 0.0)
return;
if (this.pointerX > BigImage.ActualWidth || this.pointerY > BigImage.ActualHeight)
return;
var bi = (BitmapImage)BigImage.Source;
The rotation when reaching close the edge is all about carefully calculating the rotation angle as it reaches the edge and applying that Rotation transform.
MagnifierTip.RenderTransform = new RotateTransform{Angle=<CalculatedValue>, CenterX=<CalculatedValue>, CenterY=<CalculatedValue>};
Hope this helps.
I'm going to finish a application but I have a problem about scroll selection. I has zoom in and out that's ok.I has asked so many question but no one are not reply.Help me about scroll... This picture is zooming in:
<ListView Name="lst_intro">
<ListView.ItemTemplate>
<DataTemplate>
<Image Source="{Binding link}"
Stretch="Uniform"
RenderTransformOrigin="0.5,0.5"
ManipulationDelta="img_intro_ManipulationDelta"
ManipulationMode="Scale">
<Image.RenderTransform>
<CompositeTransform/>
</Image.RenderTransform>
</Image>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
private void img_intro_ManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e)
{
Image img = sender as Image;
CompositeTransform ct = img.RenderTransform as CompositeTransform;
ct.ScaleX *= e.Delta.Scale;
ct.ScaleY *= e.Delta.Scale;
if (ct.ScaleX < mincale) ct.ScaleX = mincale;
if (ct.ScaleY < mincale) ct.ScaleY = mincale;
ScaleTransform scale=new ScaleTransform();
}
You set ManipulationMode="Scale", meaning you cannot drag it left/right/down/up - that is call a translation, set it as ManipulationMode="Scale,TranslateX,TranslateY".
In the event handler, similar to what you did for the Scale transform, you do a Translate transform.
private void img_intro_ManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e)
{
Image img = sender as Image;
CompositeTransform ct = img.RenderTransform as CompositeTransform;
if (ct == null) return;
//Scale transform
ct.ScaleX *= e.Delta.Scale;
ct.ScaleY *= e.Delta.Scale;
if (ct.ScaleX < mincale) ct.ScaleX = mincale;
if (ct.ScaleY < mincale) ct.ScaleY = mincale;
//Translate transform
ct.TranslateX += e.Delta.Translation.X;
ct.TranslateY += e.Delta.Translation.Y;
//Confine boundary
BringIntoBounds();
}
After the transformation, brings the image back if it is out of the boundary. You can detect if the image is out of boundary by comparing the values of TranslateX/TranslateY and the width/height of the boundary. The boundary is the parent of the image (it is a Grid?), you need to debug the code to determine the boundary for TranslateX and TranslateY.
public void BringIntoBounds()
{
CompositeTransform ct = img.RenderTransform as CompositeTransform;
if (ct == null) return;
//out of screen, left edge
if (ct.TranslateX < 10 - img.ActualWidth * ct.ScaleX)
{
ct.TranslateX = 10 - img.ActualWidth * ct.ScaleX;
}
//out of screen, right edge
if (ct.TranslateX > Container.ActualWidth - 10 )
{
ct.TranslateX = Container.ActualWidth - 10;
}
...do the same for Y.
}
kennyzx gave a very good answer but the part where you test if the image is out of boundaries isn't working for me... that's my solution:
private void Image_ManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e)
{
Image img = sender as Image;
CompositeTransform ct = img.RenderTransform as CompositeTransform;
ct.ScaleX *= e.Delta.Scale;
ct.ScaleY *= e.Delta.Scale;
ct.ScaleX = Math.Min(4, Math.Max(1, ct.ScaleX));
ct.ScaleY = Math.Min(4, Math.Max(1, ct.ScaleY));
ct.TranslateX += e.Delta.Translation.X;
ct.TranslateY += e.Delta.Translation.Y;
var translateY = (img.ActualHeight * ct.ScaleY - img.ActualHeight) / 2;
ct.TranslateY = Math.Min(translateY, Math.Max(0 - translateY, ct.TranslateY));
var translateX = (img.ActualWidth * ct.ScaleX - img.ActualWidth) / 2;
ct.TranslateX = Math.Min(translateX, Math.Max(0 - translateX, ct.TranslateX));
}
I’m new to windows phone development and trying to use the Microsoft.Phone.Controls.Toolkit for zooming and panning on an image. My issue is that zooming and panning only work on the first try. If I zoom/pan on the image, go elsewhere within the app and then go back to the image it doesn’t do anything. The OnPinchStarted method never runs when it is supposed to be envoked. I’ve used several different ways that I’ve found from searching here and elsewhere for the gesturelistener. Code is posted below. Before I go another route and toss out this way I wanted to see if there is something I’m missing with what I have because it works just fine the first time.
First try:
XAML:
<Grid x:Name="LayoutRoot" Background="Transparent">
<Image x:Name="Map"
Source="Images/park.png"
HorizontalAlignment="Center" VerticalAlignment="Center"
Stretch="Uniform" >
<toolkit:GestureService.GestureListener>
<toolkit:GestureListener
PinchStarted="OnPinchStarted"
PinchDelta="OnPinchDelta"
DragDelta="OnDragDelta"/>
</toolkit:GestureService.GestureListener>
<Image.RenderTransform>
<CompositeTransform
ScaleX="1" ScaleY="1"
TranslateX="0" TranslateY="0"/>
</Image.RenderTransform>
</Image>
</Grid>
CS:
// these two fields fully define the zoom state:
private double TotalImageScale = 1d;
private Point ImagePosition = new Point(0, 0);
private const double MAX_IMAGE_ZOOM = 5;
private Point _oldFinger1;
private Point _oldFinger2;
private double _oldScaleFactor;
// Initializes the zooming operation
private void OnPinchStarted(object sender, PinchStartedGestureEventArgs e)
{
_oldFinger1 = e.GetPosition(Map, 0);
_oldFinger2 = e.GetPosition(Map, 1);
_oldScaleFactor = 1;
}
//Computes the scaling and translation to correctly zoom around your fingers.
private void OnPinchDelta(object sender, PinchGestureEventArgs e)
{
var scaleFactor = e.DistanceRatio / _oldScaleFactor;
if (!IsScaleValid(scaleFactor))
return;
var currentFinger1 = e.GetPosition(Map, 0);
var currentFinger2 = e.GetPosition(Map, 1);
var translationDelta = GetTranslationDelta(
currentFinger1,
currentFinger2,
_oldFinger1,
_oldFinger2,
ImagePosition,
scaleFactor);
_oldFinger1 = currentFinger1;
_oldFinger2 = currentFinger2;
_oldScaleFactor = e.DistanceRatio;
UpdateImageScale(scaleFactor);
UpdateImagePosition(translationDelta);
}
//Moves the image around following your finger.
private void OnDragDelta(object sender, DragDeltaGestureEventArgs e)
{
var translationDelta = new Point(e.HorizontalChange, e.VerticalChange);
if (IsDragValid(1, translationDelta))
UpdateImagePosition(translationDelta);
}
//Computes the translation needed to keep the image centered between your fingers.
private Point GetTranslationDelta(
Point currentFinger1, Point currentFinger2,
Point oldFinger1, Point oldFinger2,
Point currentPosition, double scaleFactor)
{
var newPos1 = new Point(
currentFinger1.X + (currentPosition.X - oldFinger1.X) * scaleFactor,
currentFinger1.Y + (currentPosition.Y - oldFinger1.Y) * scaleFactor);
var newPos2 = new Point(
currentFinger2.X + (currentPosition.X - oldFinger2.X) * scaleFactor,
currentFinger2.Y + (currentPosition.Y - oldFinger2.Y) * scaleFactor);
var newPos = new Point(
(newPos1.X + newPos2.X) / 2,
(newPos1.Y + newPos2.Y) / 2);
return new Point(
newPos.X - currentPosition.X,
newPos.Y - currentPosition.Y);
}
//Updates the scaling factor by multiplying the delta.
private void UpdateImageScale(double scaleFactor)
{
TotalImageScale *= scaleFactor;
ApplyScale();
}
//Applies the computed scale to the image control.
private void ApplyScale()
{
((CompositeTransform)Map.RenderTransform).ScaleX = TotalImageScale;
((CompositeTransform)Map.RenderTransform).ScaleY = TotalImageScale;
}
//Updates the image position by applying the delta.
//Checks that the image does not leave empty space around its edges.
private void UpdateImagePosition(Point delta)
{
var newPosition = new Point(ImagePosition.X + delta.X, ImagePosition.Y + delta.Y);
if (newPosition.X > 0) newPosition.X = 0;
if (newPosition.Y > 0) newPosition.Y = 0;
if ((Map.ActualWidth * TotalImageScale) + newPosition.X < Map.ActualWidth)
newPosition.X = Map.ActualWidth - (Map.ActualWidth * TotalImageScale);
if ((Map.ActualHeight * TotalImageScale) + newPosition.Y < Map.ActualHeight)
newPosition.Y = Map.ActualHeight - (Map.ActualHeight * TotalImageScale);
ImagePosition = newPosition;
ApplyPosition();
}
//Applies the computed position to the image control.
private void ApplyPosition()
{
((CompositeTransform)Map.RenderTransform).TranslateX = ImagePosition.X;
((CompositeTransform)Map.RenderTransform).TranslateY = ImagePosition.Y;
}
//Resets the zoom to its original scale and position
private void ResetImagePosition()
{
TotalImageScale = 1;
ImagePosition = new Point(0, 0);
ApplyScale();
ApplyPosition();
}
//Checks that dragging by the given amount won't result in empty space around the image
private bool IsDragValid(double scaleDelta, Point translateDelta)
{
if (ImagePosition.X + translateDelta.X > 0 || ImagePosition.Y + translateDelta.Y > 0)
return false;
if ((Map.ActualWidth * TotalImageScale * scaleDelta) + (ImagePosition.X + translateDelta.X) < Map.ActualWidth)
return false;
if ((Map.ActualHeight * TotalImageScale * scaleDelta) + (ImagePosition.Y + translateDelta.Y) < Map.ActualHeight)
return false;
return true;
}
//Tells if the scaling is inside the desired range
private bool IsScaleValid(double scaleDelta)
{
return (TotalImageScale * scaleDelta >= 1) && (TotalImageScale * scaleDelta <= MAX_IMAGE_ZOOM);
}
Second Try(from http://www.wintellect.com/blogs/jprosise/building-touch-interfaces-for-windows-phones-part-4)
XAML:
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid x:Name="ContentPanel" Grid.Row="1" RenderTransformOrigin="0.5,0.5" >
<toolkit:GestureService.GestureListener>
<toolkit:GestureListener PinchDelta="OnPinchDelta"
PinchStarted="OnPinchStarted" DragDelta="OnDragDelta" />
</toolkit:GestureService.GestureListener>
<Grid.RenderTransform>
<CompositeTransform x:Name="HambyTransform" />
</Grid.RenderTransform>
<Image Source="Images/trails.png"
HorizontalAlignment="Center" VerticalAlignment="Center"
Stretch="Uniform" />
</Grid>
</Grid>
CS:
private double _cx, _cy;
private void OnPinchStarted(object sender, PinchStartedGestureEventArgs e)
{
_cx = HambyTransform.ScaleX;
_cy = HambyTransform.ScaleY;
}
//scale the map
private void OnPinchDelta(object sender, PinchGestureEventArgs e)
{
//compute the new scaling factors
double cx = _cx * e.DistanceRatio;
double cy = _cy * e.DistanceRatio;
// If they're between 1.0 and 4.0, inclusive, apply them
if (cx >= 1.0 && cx <= 4.0 && cy >= 1.0 && cy <= 4.0)
{
HambyTransform.ScaleX = cx;
HambyTransform.ScaleY = cy;
}
}
private void OnDragDelta(object sender, DragDeltaGestureEventArgs e)
{
HambyTransform.TranslateX += e.HorizontalChange;
HambyTransform.TranslateY += e.VerticalChange;
}
After many attempts to find a fix, which I never did, I started all over with a new project and the issue went away. Zoom/pan works fine. I don't know what happened or if something internal in my project was corrupt, but its working now. I hope this helps anybody who runs into the same issue. I had to do alot of copying/pasting code, but it worked.
I am using Microsoft Chart control in my project and I want to enable zooming feature in Chart Control by using Mouse Wheel, how can I achieve this?
but user don't have to click on chart, It should be like if mouse position is on my Chart than from that point onward by mouse wheel rolling it can zoom in / out
You'll want to use the MouseWheel event.
First make both axes of your chart zoomable:
chart1.ChartAreas[0].AxisX.ScaleView.Zoomable = true;
chart1.ChartAreas[0].AxisY.ScaleView.Zoomable = true;
And assign the event:
chart1.MouseWheel += chart1_MouseWheel;
Then in the event handler:
private void chart1_MouseWheel(object sender, MouseEventArgs e)
{
var chart = (Chart)sender;
var xAxis = chart.ChartAreas[0].AxisX;
var yAxis = chart.ChartAreas[0].AxisY;
try
{
if (e.Delta < 0) // Scrolled down.
{
xAxis.ScaleView.ZoomReset();
yAxis.ScaleView.ZoomReset();
}
else if (e.Delta > 0) // Scrolled up.
{
var xMin = xAxis.ScaleView.ViewMinimum;
var xMax = xAxis.ScaleView.ViewMaximum;
var yMin = yAxis.ScaleView.ViewMinimum;
var yMax = yAxis.ScaleView.ViewMaximum;
var posXStart = xAxis.PixelPositionToValue(e.Location.X) - (xMax - xMin) / 4;
var posXFinish = xAxis.PixelPositionToValue(e.Location.X) + (xMax - xMin) / 4;
var posYStart = yAxis.PixelPositionToValue(e.Location.Y) - (yMax - yMin) / 4;
var posYFinish = yAxis.PixelPositionToValue(e.Location.Y) + (yMax - yMin) / 4;
xAxis.ScaleView.Zoom(posXStart, posXFinish);
yAxis.ScaleView.Zoom(posYStart, posYFinish);
}
}
catch { }
}
The e.Delta property tells you how many wheel "scrolls" you've done, and can be useful.
Scrolling out at all will zoom out the whole way.
There's probably a cleaner way of doing this, but there it is.
Hope this helps!
I modificated code from above and added a reverse zooming using Stack.
private class ZoomFrame
{
public double XStart { get; set; }
public double XFinish { get; set; }
public double YStart { get; set; }
public double YFinish { get; set; }
}
private readonly Stack<ZoomFrame> _zoomFrames = new Stack<ZoomFrame>();
private void chart1_MouseWheel(object sender, MouseEventArgs e)
{
var chart = (Chart)sender;
var xAxis = chart.ChartAreas[0].AxisX;
var yAxis = chart.ChartAreas[0].AxisY;
try
{
if (e.Delta < 0)
{
if (0 < _zoomFrames.Count)
{
var frame = _zoomFrames.Pop();
if (_zoomFrames.Count == 0)
{
xAxis.ScaleView.ZoomReset();
yAxis.ScaleView.ZoomReset();
}
else
{
xAxis.ScaleView.Zoom(frame.XStart, frame.XFinish);
yAxis.ScaleView.Zoom(frame.YStart, frame.YFinish);
}
}
}
else if (e.Delta > 0)
{
var xMin = xAxis.ScaleView.ViewMinimum;
var xMax = xAxis.ScaleView.ViewMaximum;
var yMin = yAxis.ScaleView.ViewMinimum;
var yMax = yAxis.ScaleView.ViewMaximum;
_zoomFrames.Push(new ZoomFrame { XStart = xMin, XFinish = xMax, YStart = yMin, YFinish = yMax });
var posXStart = xAxis.PixelPositionToValue(e.Location.X) - (xMax - xMin) / 4;
var posXFinish = xAxis.PixelPositionToValue(e.Location.X) + (xMax - xMin) / 4;
var posYStart = yAxis.PixelPositionToValue(e.Location.Y) - (yMax - yMin) / 4;
var posYFinish = yAxis.PixelPositionToValue(e.Location.Y) + (yMax - yMin) / 4;
xAxis.ScaleView.Zoom(posXStart, posXFinish);
yAxis.ScaleView.Zoom(posYStart, posYFinish);
}
}
catch { }
}
Hope this helps!
I modificated code from above and added a reverse zooming. So when you rotate a mouse wheel back the chart zoom out. Also i don't recommend use 2^n as divider of the interval because it cause lag.
numberOfZoom - counter of Zooming
private void Chart1_MouseWheel(object sender, MouseEventArgs e)
{
var chart = (Chart)sender;
var xAxis = chart.ChartAreas[0].AxisX;
var yAxis = chart.ChartAreas[0].AxisY;
var xMin = xAxis.ScaleView.ViewMinimum;
var xMax = xAxis.ScaleView.ViewMaximum;
var yMin = yAxis.ScaleView.ViewMinimum;
var yMax = yAxis.ScaleView.ViewMaximum;
int IntervalX = 3;
int IntervalY = 3;
try
{
if (e.Delta < 0 && numberOfZoom > 0) // Scrolled down.
{
var posXStart = xAxis.PixelPositionToValue(e.Location.X) - IntervalX *2/ Math.Pow(2, numberOfZoom);
var posXFinish = xAxis.PixelPositionToValue(e.Location.X) + IntervalX *2/ Math.Pow(2, numberOfZoom);
var posYStart = yAxis.PixelPositionToValue(e.Location.Y) - IntervalY*2 / Math.Pow(2, numberOfZoom);
var posYFinish = yAxis.PixelPositionToValue(e.Location.Y) + IntervalY*2 / Math.Pow(2, numberOfZoom);
if (posXStart < 0) posXStart = 0;
if (posYStart < 0) posYStart = 0;
if (posYFinish > yAxis.Maximum) posYFinish = yAxis.Maximum;
if (posXFinish > xAxis.Maximum) posYFinish = xAxis.Maximum;
xAxis.ScaleView.Zoom(posXStart, posXFinish);
yAxis.ScaleView.Zoom(posYStart, posYFinish);
numberOfZoom--;
}else if (e.Delta < 0 && numberOfZoom == 0) //Last scrolled dowm
{
yAxis.ScaleView.ZoomReset();
xAxis.ScaleView.ZoomReset();
}
else if (e.Delta > 0) // Scrolled up.
{
var posXStart = xAxis.PixelPositionToValue(e.Location.X) - IntervalX / Math.Pow(2, numberOfZoom);
var posXFinish = xAxis.PixelPositionToValue(e.Location.X) + IntervalX / Math.Pow(2, numberOfZoom);
var posYStart = yAxis.PixelPositionToValue(e.Location.Y) - IntervalY / Math.Pow(2, numberOfZoom);
var posYFinish = yAxis.PixelPositionToValue(e.Location.Y) + IntervalY / Math.Pow(2, numberOfZoom);
xAxis.ScaleView.Zoom(posXStart, posXFinish);
yAxis.ScaleView.Zoom(posYStart, posYFinish);
numberOfZoom++;
}
if (numberOfZoom < 0) numberOfZoom = 0;
}
catch { }
}
Waaaay late to the party, but I had this challenge myself today.
I am sure the following can be improved, still, but it's what I came up with.
I tested this with .net 4.5.2 and 4.6, so I am not sure if it works with older frameworks.
I've combined a couple of answers from way in the past and made the following:
using System;
using System.Windows.Forms;
using System.Windows.Forms.DataVisualization.Charting;
namespace ExampleApp
{
public partial class Form1 : Form
{
private const float CZoomScale = 4f;
private int FZoomLevel = 0;
public Form1()
{
InitializeComponent();
chart1.MouseWheel += Chart1_MouseWheel;
}
private void Chart1_MouseEnter(object sender, EventArgs e)
{
if (!chart1.Focused)
chart1.Focus();
}
private void Chart1_MouseLeave(object sender, EventArgs e)
{
if (chart1.Focused)
chart1.Parent.Focus();
}
private void Chart1_MouseWheel(object sender, MouseEventArgs e)
{
try {
Axis xAxis = chart1.ChartAreas[0].AxisX;
double xMin = xAxis.ScaleView.ViewMinimum;
double xMax = xAxis.ScaleView.ViewMaximum;
double xPixelPos = xAxis.PixelPositionToValue(e.Location.X);
if (e.Delta < 0 && FZoomLevel > 0) {
// Scrolled down, meaning zoom out
if (--FZoomLevel <= 0) {
FZoomLevel = 0;
xAxis.ScaleView.ZoomReset();
} else {
double xStartPos = Math.Max(xPixelPos - (xPixelPos - xMin) * CZoomScale, 0);
double xEndPos = Math.Min(xStartPos + (xMax - xMin) * CZoomScale, xAxis.Maximum);
xAxis.ScaleView.Zoom(xStartPos, xEndPos);
}
} else if (e.Delta > 0) {
// Scrolled up, meaning zoom in
double xStartPos = Math.Max(xPixelPos - (xPixelPos - xMin) / CZoomScale, 0);
double xEndPos = Math.Min(xStartPos + (xMax - xMin) / CZoomScale, xAxis.Maximum);
xAxis.ScaleView.Zoom(xStartPos, xEndPos);
FZoomLevel++;
}
} catch { }
}
}
}