Hi,
after two days lost by attempting to resolve this problem, I think that i need help to solve it... ;)
I've made a simple application with a Canvas into the main window. For testing, this Canvas is very large (10.000x10.000) and centered. I placed some controllers (buttons and rectangles).
As mentionned in the title, I have a problem with the zoom and the panning. I use a transformGroup applying on the canvas and a translateTransform and scaleTransform for the zooming/panning functions.
For the panning, it's working. I just have a little problem when I click on a shape rather than the background of the canvas but it's not signifiant for now (if you have some tips for it...) .
For the zooming, I scale the canvas and it's working very well but it doesn't scale on the mouse pointer. I have a problem with (I think) the zoom origin. I tried so many things (reset the transform group, use the mouse pointer as origin, calculate the origin with the width of the windows and the canvas width, ...)
I've read also many other posts on this subject but I still have the problem and now I don't know what I can do to solve it.
<Window x:Class="PanZoomingCanvas.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="800" Width="1200" WindowState="Maximized" Background="#FF2A2A2A"><Canvas x:Name="MainCanvas" Height="10000" Margin="-5000,-5000,-5000,-5000" Width="10000" RenderTransformOrigin="0,0" MouseLeftButtonDown="MainCanvas_MouseLeftButtonDown" MouseLeftButtonUp="MainCanvas_MouseLeftButtonUp" MouseMove="MainCanvas_MouseMove" MouseWheel="MainCanvas_MouseWheel" ><Canvas.Background><VisualBrush TileMode="FlipXY" Viewport="0,0,25,25" ViewportUnits="Absolute" Viewbox="0,0,50,50" ViewboxUnits="Absolute"><VisualBrush.Visual><Rectangle Stroke="#FF000000" StrokeThickness="1" Height="50" Width="50"></Rectangle></VisualBrush.Visual></VisualBrush></Canvas.Background><Rectangle Canvas.Left="4949" Canvas.Top="4926" Width="81" Height="132" Fill="#FF32A0FB" /><Button Content="Button" Canvas.Left="5131" Canvas.Top="5102" Width="81" Height="32"/><Button Content="Button" Canvas.Left="4778" Canvas.Top="5102" Width="81" Height="32"/><Button Content="Button" Canvas.Left="5131" Canvas.Top="4852" Width="81" Height="32"/><Button Content="Button" Canvas.Left="4778" Canvas.Top="4852" Width="81" Height="32"/><TextBox x:Name="myToolTip" ToolTipService.InitialShowDelay="5000" ToolTipService.ShowDuration="2000" ToolTipService.BetweenShowDelay="10000" ToolTip="This is a tool tip." Panel.ZIndex="100" /><Rectangle Canvas.Left="4631" Canvas.Top="4802" Width="48" Height="48" Fill="#FFFB3232" /><Rectangle Canvas.Left="5303" Canvas.Top="4802" Width="48" Height="48" Fill="#FFFB3232" /><Rectangle Canvas.Left="4631" Canvas.Top="5134" Width="48" Height="48" Fill="#FFFB3232" /><Rectangle Canvas.Left="5303" Canvas.Top="5134" Width="48" Height="48" Fill="#FFFB3232" /></Canvas></Window>
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace PanZoomingCanvas { /// <summary> /// Logique d'interaction pour MainWindow.xaml /// </summary> public partial class MainWindow : Window { private Point _pointOnClick; // Click Position for panning private ScaleTransform _scaleTransform; private TranslateTransform _translateTransform; private TransformGroup _transformGroup; public MainWindow() { InitializeComponent(); _translateTransform = new TranslateTransform(); _scaleTransform = new ScaleTransform(); _transformGroup = new TransformGroup(); _transformGroup.Children.Add(_translateTransform); _transformGroup.Children.Add(_scaleTransform); MainCanvas.RenderTransform = _transformGroup; } private void MainCanvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { //Capture Mouse MainCanvas.CaptureMouse(); //Store click position relation to Parent of the canvas _pointOnClick = e.GetPosition((FrameworkElement)MainCanvas.Parent); } private void MainCanvas_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) { //Release Mouse Capture MainCanvas.ReleaseMouseCapture(); //Set cursor by default Mouse.OverrideCursor = null; } private void MainCanvas_MouseMove(object sender, MouseEventArgs e) { //ToolTip for informations showToolTip(e); //Return if mouse is not captured if (!MainCanvas.IsMouseCaptured) return; //Point on move from Parent Point pointOnMove = e.GetPosition((FrameworkElement)MainCanvas.Parent); //set TranslateTransform _translateTransform.X = MainCanvas.RenderTransform.Value.OffsetX - (_pointOnClick.X - pointOnMove.X); _translateTransform.Y = MainCanvas.RenderTransform.Value.OffsetY - (_pointOnClick.Y - pointOnMove.Y); //Update pointOnClic _pointOnClick = e.GetPosition((FrameworkElement)MainCanvas.Parent); } private void MainCanvas_MouseWheel(object sender, MouseWheelEventArgs e) { //Point de la souris Point mousePosition = e.GetPosition(MainCanvas); //Actual Zoom double zoomNow = Math.Round(MainCanvas.RenderTransform.Value.M11, 1); //ZoomScale double zoomScale = 0.1; //Positive or negative zoom double valZoom = e.Delta > 0 ? zoomScale : -zoomScale; //Point de la souris pour le panning et zoom/dezoom Point pointOnMove = e.GetPosition((FrameworkElement)MainCanvas.Parent); //RenderTransformOrigin (doesn't fully working) MainCanvas.RenderTransformOrigin = new Point(mousePosition.X / MainCanvas.ActualWidth, mousePosition.Y / MainCanvas.ActualHeight); //Appel du zoom Zoom(new Point(mousePosition.X, mousePosition.Y), zoomNow + valZoom); } /// Zoom function private void Zoom(Point point, double scale) { //Calcul des centres selon la position de la souris double centerX = (point.X - _translateTransform.X) / _scaleTransform.ScaleX; double centerY = (point.Y - _translateTransform.Y) / _scaleTransform.ScaleY; //Mise à l'échelle _scaleTransform.ScaleX = scale; _scaleTransform.ScaleY = scale; //Retablissement du translate pour éviter un décalage _translateTransform.X = point.X - centerX * _scaleTransform.ScaleX; _translateTransform.Y = point.Y - centerY * _scaleTransform.ScaleY; } /// Just For ToolTip private void showToolTip(MouseEventArgs e) { Point currentPos = e.GetPosition(MainCanvas); Point currentPos2 = e.GetPosition((FrameworkElement)MainCanvas.Parent); myToolTip.RenderTransform = new TranslateTransform(currentPos.X + 20, currentPos.Y); myToolTip.Text = "Cursor position from MainCanvas : X=" + currentPos.X + ";Y=" + currentPos.Y + "\n"; myToolTip.Text += "Cursor position from Parent : X=" + currentPos2.X + ";Y=" + currentPos2.Y + "\n"; myToolTip.Text += "OffsetXY of MainCanvas: X=" + MainCanvas.RenderTransform.Value.OffsetX + ";Y=" + MainCanvas.RenderTransform.Value.OffsetY + "\n"; myToolTip.Text += "Size of MainCanvas : Width=" + MainCanvas.ActualWidth + ";Height=" + MainCanvas.ActualWidth + "\n"; myToolTip.Text += "Size of Parent: Width=" + ((FrameworkElement)MainCanvas.Parent).ActualWidth + ";Height=" + ((FrameworkElement)MainCanvas.Parent).ActualHeight; } } }
The complete project: https://onedrive.live.com/?cid=c36ccfc0e7a03756&id=C36CCFC0E7A03756!1898&ithint=file,zip&authkey=!APcSZNKqaHcOwhM
I'm a beginner with the WPF ans maybe I don't use the good controllers or maybe there is another way to do it...
Thank you for your help :)