I am currently working with c# PictureBox and I have made handsfree drawing in picture box which are irregular shapes and horizontal lines. how to move the selected irregular shape with the mouse move.
public class IrregularShape
{
public Color ShapeColor { get; set; }
public Point Start { get; set; }
public Point End { get; set; }
public List<Point> points = new List<Point>();
}
I have the selected shape in mouse down with start,end and a list of points of that shape.
Glad to have finally found this.
In mouse down write this code:
isdraggingshape = true;
if (selectedshape != null)
{
OffsetX = selectedshape.Start.X - e.Location.X;
OffsetY = selectedshape.Start.Y - e.Location.Y;
}
in mouse move:
if (isdraggingshape == true)
{
if (OffsetX != null && OffsetY != null)
{
if (e.Button == MouseButtons.Left)
{ int new_x1 = e.X + OffsetX;
int new_y1 = e.Y + OffsetY;
int dx = new_x1 - selectedshape.Start.X;
int dy = new_y1 - selectedshape.Start.Y;
if (dx == 0 && dy == 0) return;
// Move the shape
for (int i = 0; i < selectedshape.points.Count; i++)
{
selectedshape.points[i] = new Point(selectedshape.points[i].X + dx,selectedshape.points[i].Y + dy);
}
// Redraw.
ImagepictureBox.Invalidate();
}
Related
Problem - I'm writing a program that draws graphics, and zooming is one of the features. Currently, a picturebox is placed on a panel, and the picturebox has vertical and horizontal scroll bars on the right and bottom. How to combine scrollbar with mouse wheel zooming? And I'm not sure if I should use paint to draw the graphics or set a bitmap to draw the graphics onto it?
Expected - When the mouse wheel is scrolled, the entire canvas(picturebox) include drawn graphics are scaled according to the current mouse position as the center (the horizontal and vertical scroll bars change according to the zoom center). When the mouse wheel is pressed and moved, the canvas can be dragged freely.
Expected as follows:
The initial code
private List<Point> _points;
private int _pointRadius = 50;
private float _scale = 1f;
private float _offsetX = 0f;
private float _offsetY = 0f;
private void picturebox_MouseDown(object sender, MouseEventArgs e)
{
_points.Add(e.Location);
}
private void picturebox_MouseWheel(object sender, MouseEvnetArgs e)
{
if(e.Delta < 0)
{
_scale += 0.1f;
_offsetX = e.X * (1f - _scale);
_offsetY = e.X * (1f - _scale);
}
else
{
_scale -= 0.1f;
_offsetX = e.X * (1f - _scale);
_offsetY = e.X * (1f - _scale);
}
picturebox.Invalidate();
}
private void picturebox_Paint(object sender, PaintEventArgs e)
{
e.Graphics.TranslateTransform(_offsetX, _offsetY);
e.Graphics.ScaleTransform(_scaleX, _scaleY);
foreach (Point p in _points)
{
e.Graphics.FillEllipse(Brushes.Black, p.X, - _pointRadius, p.Y - _pointRadius, 2 * _pointRadius, 2 * _pointRadius);
}
}
Hope the answer is modified based on the initial code.
Thanks in advance to everyone who helped me.
Would it be easier if I drew the graphics on a Bitmap?
Considering the nature of your task and the already implemented solutions in my ImageViewer I created a solution that draws the result in a Metafile, which is both elegant, consumes minimal memory and allows zooming without quality issues.
Here is the stripped version of my ImageViewer:
public class MetafileViewer : Control
{
private HScrollBar sbHorizontal = new HScrollBar { Visible = false };
private VScrollBar sbVertical = new VScrollBar { Visible = false };
private Metafile? image;
private Size imageSize;
private Rectangle targetRectangle;
private Rectangle clientRectangle;
private float zoom = 1;
private bool sbHorizontalVisible;
private bool sbVerticalVisible;
private int scrollFractionVertical;
public MetafileViewer()
{
Controls.AddRange(new Control[] { sbHorizontal, sbVertical });
sbHorizontal.ValueChanged += ScrollbarValueChanged;
sbVertical.ValueChanged += ScrollbarValueChanged;
}
void ScrollbarValueChanged(object? sender, EventArgs e) => Invalidate();
public Metafile? Image
{
get => image;
set
{
image = value;
imageSize = image?.Size ?? default;
InvalidateLayout();
}
}
public bool TryTranslate(Point mouseCoord, out PointF canvasCoord)
{
canvasCoord = default;
if (!targetRectangle.Contains(mouseCoord))
return false;
canvasCoord = new PointF((mouseCoord.X - targetRectangle.X) / zoom, (mouseCoord.Y - targetRectangle.Y) / zoom);
if (sbHorizontalVisible)
canvasCoord.X += sbHorizontal.Value / zoom;
if (sbVerticalVisible)
canvasCoord.Y += sbVertical.Value / zoom;
return true;
}
private void InvalidateLayout()
{
Invalidate();
if (imageSize.IsEmpty)
{
sbHorizontal.Visible = sbVertical.Visible = sbHorizontalVisible = sbVerticalVisible = false;
targetRectangle = Rectangle.Empty;
return;
}
Size clientSize = ClientSize;
if (clientSize.Width < 1 || clientSize.Height < 1)
{
targetRectangle = Rectangle.Empty;
return;
}
Size scaledSize = imageSize.Scale(zoom);
// scrollbars visibility
sbHorizontalVisible = scaledSize.Width > clientSize.Width
|| scaledSize.Width > clientSize.Width - SystemInformation.VerticalScrollBarWidth && scaledSize.Height > clientSize.Height;
sbVerticalVisible = scaledSize.Height > clientSize.Height
|| scaledSize.Height > clientSize.Height - SystemInformation.HorizontalScrollBarHeight && scaledSize.Width > clientSize.Width;
if (sbHorizontalVisible)
clientSize.Height -= SystemInformation.HorizontalScrollBarHeight;
if (sbVerticalVisible)
clientSize.Width -= SystemInformation.VerticalScrollBarWidth;
if (clientSize.Width < 1 || clientSize.Height < 1)
{
targetRectangle = Rectangle.Empty;
return;
}
Point clientLocation = Point.Empty;
var targetLocation = new Point((clientSize.Width >> 1) - (scaledSize.Width >> 1),
(clientSize.Height >> 1) - (scaledSize.Height >> 1));
// both scrollbars
if (sbHorizontalVisible && sbVerticalVisible)
{
sbHorizontal.Dock = sbVertical.Dock = DockStyle.None;
sbHorizontal.Width = clientSize.Width;
sbHorizontal.Top = clientSize.Height;
sbHorizontal.Left = 0;
sbVertical.Height = clientSize.Height;
sbVertical.Left = clientSize.Width;
}
// horizontal scrollbar
else if (sbHorizontalVisible)
sbHorizontal.Dock = DockStyle.Bottom;
// vertical scrollbar
else if (sbVerticalVisible)
sbVertical.Dock = DockStyle.Right;
// adjust scrollbar values
if (sbHorizontalVisible)
{
sbHorizontal.Minimum = targetLocation.X;
sbHorizontal.Maximum = targetLocation.X + scaledSize.Width;
sbHorizontal.LargeChange = clientSize.Width;
sbHorizontal.SmallChange = 32;
sbHorizontal.Value = Math.Min(sbHorizontal.Value, sbHorizontal.Maximum - sbHorizontal.LargeChange);
}
if (sbVerticalVisible)
{
sbVertical.Minimum = targetLocation.Y;
sbVertical.Maximum = targetLocation.Y + scaledSize.Height;
sbVertical.LargeChange = clientSize.Height;
sbVertical.SmallChange = 32;
sbVertical.Value = Math.Min(sbVertical.Value, sbVertical.Maximum - sbVertical.LargeChange);
}
sbHorizontal.Visible = sbHorizontalVisible;
sbVertical.Visible = sbVerticalVisible;
clientRectangle = new Rectangle(clientLocation, clientSize);
targetRectangle = new Rectangle(targetLocation, scaledSize);
if (sbVerticalVisible)
clientRectangle.X = SystemInformation.VerticalScrollBarWidth;
}
protected override void OnSizeChanged(EventArgs e)
{
base.OnSizeChanged(e);
InvalidateLayout();
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
if (image == null || e.ClipRectangle.Width <= 0 || e.ClipRectangle.Height <= 0)
return;
if (targetRectangle.IsEmpty)
InvalidateLayout();
if (targetRectangle.IsEmpty)
return;
Graphics g = e.Graphics;
g.IntersectClip(clientRectangle);
Rectangle dest = targetRectangle;
if (sbHorizontalVisible)
dest.X -= sbHorizontal.Value;
if (sbVerticalVisible)
dest.Y -= sbVertical.Value;
g.DrawImage(image, dest);
g.DrawRectangle(SystemPens.ControlText, Rectangle.Inflate(targetRectangle, 1, 1));
}
protected override void OnMouseWheel(MouseEventArgs e)
{
base.OnMouseWheel(e);
switch (ModifierKeys)
{
// zoom
case Keys.Control:
float delta = (float)e.Delta / SystemInformation.MouseWheelScrollDelta / 5;
if (delta.Equals(0f))
return;
delta += 1;
SetZoom(zoom * delta);
break;
// vertical scroll
case Keys.None:
VerticalScroll(e.Delta);
break;
}
}
private void VerticalScroll(int delta)
{
// When scrolling by mouse, delta is always +-120 so this will be a small change on the scrollbar.
// But we collect the fractional changes caused by the touchpad scrolling so it will not be lost either.
int totalDelta = scrollFractionVertical + delta * sbVertical.SmallChange;
scrollFractionVertical = totalDelta % SystemInformation.MouseWheelScrollDelta;
int newValue = sbVertical.Value - totalDelta / SystemInformation.MouseWheelScrollDelta;
SetValueSafe(sbVertical, newValue);
}
internal static void SetValueSafe(ScrollBar scrollBar, int value)
{
if (value < scrollBar.Minimum)
value = scrollBar.Minimum;
else if (value > scrollBar.Maximum - scrollBar.LargeChange + 1)
value = scrollBar.Maximum - scrollBar.LargeChange + 1;
scrollBar.Value = value;
}
private void SetZoom(float value)
{
const float maxZoom = 10f;
float minZoom = image == null ? 1f : 1f / Math.Min(imageSize.Width, imageSize.Height);
if (value < minZoom)
value = minZoom;
if (value > maxZoom)
value = maxZoom;
if (zoom.Equals(value))
return;
zoom = value;
InvalidateLayout();
}
}
And then the updated version of your initial code (add a new point by right click, zoom by Ctrl + mouse scroll):
public partial class RenderMetafileForm : Form
{
private static Size canvasSize = new Size(300, 200);
private List<PointF> points = new List<PointF>();
private const float pointRadius = 5;
public RenderMetafileForm()
{
InitializeComponent();
metafileViewer.MouseClick += MetafileViewer_MouseClick;
UpdateMetafile();
}
private void MetafileViewer_MouseClick(object? sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Right && metafileViewer.TryTranslate(e.Location, out var coord))
{
points.Add(coord);
UpdateMetafile();
}
}
private void UpdateMetafile()
{
Graphics refGraph = Graphics.FromHwnd(IntPtr.Zero);
IntPtr hdc = refGraph.GetHdc();
Metafile result;
try
{
result = new Metafile(hdc, new Rectangle(Point.Empty, canvasSize), MetafileFrameUnit.Pixel, EmfType.EmfOnly, "Canvas");
using (var g = Graphics.FromImage(result))
{
foreach (PointF point in points)
g.FillEllipse(Brushes.Navy, point.X - pointRadius, point.Y - pointRadius, pointRadius * 2, pointRadius * 2);
}
}
finally
{
refGraph.ReleaseHdc(hdc);
refGraph.Dispose();
}
Metafile? previous = metafileViewer.Image;
metafileViewer.Image = result;
previous?.Dispose();
}
}
Result:
⚠️ Note: I did not add panning by keyboard or by grabbing the image but you can extract those from the original ImageViewer. Also, I removed DPI-aware scaling but see the ScaleSize extensions in the linked project.
I'm creating GMap.NET app and added some custom markers on it. Is it possible to make them scale depending on the zoom level of the map?
You will need to create a custom GMapMarker and use the zoom level in the OnRender() to scale the image. Each zoom level has a different ratio but luckily for you, I have mesured them all. Not sure if the ratio changes with different map providers?
You can set the x/y scale and heading using a seperate thread and it will update in real time.
The "defaultZoomLevel" will set the zoom level at which the image has a 1:1 ratio. I have only tried the value 16, not sure it will work for other values you may need to calculate all the ratios for each value or extract the curve from the data and use that to scale the ratio.
Use this to set the zoom level when it changes:
private void Gmap_OnMapZoomChanged()
{
//trackBar_Zoom.Value = (int)Gmap.Zoom;
//label_Zoom.Text = "Zoom: " + trackBar_Zoom.Value.ToString();
SelectedOverlay?.SetZoomLevel(Gmap.Zoom);
}
Use it like:
SelectedOverlay = new GMapMarkerImageOverlay(Gmap.Position, 0, 1, 1, Gmap.Zoom, Resources.GreySquare);
Overlay_ImageOverlays.Markers.Add(SelectedOverlay);
And this is the MapMarker:
public class GMapMarkerImageOverlay : GMapMarker
{
private readonly double deg2rad = 3.14159265 / 180.0;
private readonly double rad2deg = 180 / 3.14159265;
private readonly float defaultZoomLevel = 16;
private float zoomRatio = 1f;
private float heading = 0;
private double zoom = 0;
private float scaleX = 0;
private float scaleY = 0;
private Bitmap overlayImage;
private void SetZoomRatio()
{
if (zoom < 12)
{
zoomRatio = 0.045f;
}
else if (zoom == 12)
{
zoomRatio = 0.08f;
}
else if (zoom == 13)
{
zoomRatio = 0.155f;
}
else if (zoom == 14)
{
zoomRatio = 0.285f;
}
else if (zoom == 15)
{
zoomRatio = 0.53f;
}
else if (zoom == 16)
{
zoomRatio = 1f;
}
else if (zoom == 17)
{
zoomRatio = 1.88f;
}
else if (zoom == 18)
{
zoomRatio = 3.55f;
}
else if (zoom == 19)
{
zoomRatio = 6.75f;
}
else if (zoom == 20)
{
zoomRatio = 11.5f;
}
else
{
zoomRatio = 11.5f;
}
}
public GMapMarkerImageOverlay(PointLatLng p, float heading, float scaleX, float scaleY, double zoom, Bitmap image)
: base(p)
{
overlayImage = image;
this.heading = heading;
this.scaleX = scaleX;
this.scaleY = scaleY;
this.zoom = zoom;
SetZoomRatio();
}
internal void SetPosition(PointLatLng position)
{
//Position = position;
//LocalPosition = position;
}
public void SetHeading(float h)
{
heading = h;
}
public void SetZoomLevel(double z)
{
zoom = z;
SetZoomRatio();
//Util.Log($"Zoom level: {z}");
}
public void SetScaleX(float x)
{
scaleX = x;
}
public void SetScaleY(float y)
{
scaleY = y;
}
public void SetRatio(float r)
{
zoomRatio = r;
}
public override void OnRender(Graphics g)
{
try
{
var temp = g.Transform;
g.TranslateTransform(LocalPosition.X, LocalPosition.Y);
float ratio = (float)zoom / defaultZoomLevel;
ratio *= zoomRatio;
g.ScaleTransform(scaleX*ratio, scaleY*ratio);
base.ToolTipMode = MarkerTooltipMode.OnMouseOver;
base.ToolTipText = $"Ratio:{ratio}";
// anti NaN
try
{
g.RotateTransform(heading);
}
catch
{
}
var sIcon = overlayImage;
sIcon = new Bitmap(sIcon, sIcon.Width / 1, sIcon.Height / 1);
g.DrawImageUnscaled(sIcon, sIcon.Width / -2, sIcon.Height / -2);
g.Transform = temp;
}
catch (Exception ex)
{
//Util.Log(ex);
}
}
}
I want to realize method "Draw" of class Polygon
I have WindForms project, form, and the pictureBox1,
I want that "Draw" drawing polygon in pictureBox1 and I have opportunity to move image
I don't konw how to realize it. Help, please.
public class Polygon
{
public Point[] vertexes { get; protected set; }
public Polygon(params int[] vertex)
{
if (vertex == null || vertex.Length <= 2)
throw new Exception("someText");
if (vertex.Length % 2 != 0)
throw new Exception("someText");
vertexes = new Point[vertex.Length / 2];
ColorContour = System.Drawing.Color.DarkRed;
Priming = false;
for (int i = 0, j = 0; i < vertexes.Length; i++, j += 2)
vertexes[i] = new Point(vertex[j], vertex[j + 1]);
vertexes = Point.Sort(vertexes);
if (vertexes == null || vertexes.Length <= 2)
throw new Exception("someText");
}
public double Perimetr
{
get
{
double res = 0;
for (int i = 1; i < vertexes.Length; i++)
res += Point.Length(vertexes[i - 1], vertexes[i]);
return res;
}
}
public override void Move(int deltax, int deltay)
{
vertexes[0].x = deltax;
vertexes[0].y = deltay;
for (int i = 1; i < vertexes.Length; i++)
{
vertexes[i].x -= deltax;
vertexes[i].y -= deltay;
}
}
public void Zoom(double size)
{
if (size == 0)
return;
Point firstP = new Point(vertexes[0].x, vertexes[0].y);
Point Center = Point.CentrMass(vertexes);
for (int i = 0; i < vertexes.Length; ++i)
{
vertexes[i].x = Convert.ToInt32(size * (vertexes[i].x - Center.x) + Center.x);
vertexes[i].y = Convert.ToInt32(size * (vertexes[i].y - Center.y) + Center.y);
}
Move(firstP.x, firstP.y);
}
public void Draw( ??)
{
**????**
}
publicabstract double Square { get; }
You need to take in a System.Drawing.Graphics as a parameter, and call Graphics.DrawPolygon() function. Then in the picturebox, override or implement the OnPaint() event, and call your draw function with the Graphics you receive as a parameter (child of the eventargs) in OnPaint().
so i have drawn a few objects , circles squares or even lines. This is the code i use to draw the images:
Graphics surface = this.splitContainer1.Panel2.CreateGraphics();
Pen pen1 = new Pen(ColorR.BackColor, float.Parse(boxWidth.Text));
switch (currentObject)
{
case "line":
if (step == 1)
{
splitContainer1.Panel2.Focus();
one.X = e.X;
one.Y = e.Y;
boxX.Text = one.X.ToString();
boxY.Text = one.Y.ToString();
step = 2;
}
else
{
two.X = e.X;
two.Y = e.Y;
boxX2.Text = two.X.ToString();
boxY2.Text = two.Y.ToString();
surface.DrawLine(pen1, one, two);
step = 1;
}
break;
case "circle":
if (step == 1)
{
boxX.Text = e.X.ToString();
boxY.Text = e.Y.ToString();
step = 2;
}
else
{
int tempX = int.Parse(boxX.Text);
int tempY = int.Parse(boxY.Text);
int tempX2 = e.X;
int tempY2 = e.Y;
int sideX, sideY;
if (tempX > tempX2)
{
sideX = tempX - tempX2;
}
else
{
sideX = tempX2 - tempX;
}
if (tempY > tempY2)
{
sideY = tempY - tempY2;
}
else
{
sideY = tempY2 - tempY;
}
double tempRadius;
tempRadius = Math.Sqrt(sideX * sideX + sideY * sideY);
tempRadius *= 2;
bWidth.Text = bHeight.Text = Convert.ToInt32(tempRadius).ToString();
surface.DrawEllipse(
pen1,
int.Parse(boxX.Text) - int.Parse(bWidth.Text) / 2,
int.Parse(boxY.Text) - int.Parse(bHeight.Text) / 2,
float.Parse(bWidth.Text), float.Parse(bHeight.Text));
step = 1;
}
break;
case "square":
if (step == 1)
{
boxX.Text = e.X.ToString();
boxY.Text = e.Y.ToString();
step = 2;
}
else if (step == 2)
{
int tempX = e.X;
if (tempX > int.Parse(boxX.Text))
{
bWidth.Text = (tempX - int.Parse(boxX.Text)).ToString();
}
else
{
bWidth.Text = (int.Parse(boxX.Text) - tempX).ToString();
}
step = 3;
}
else
{
int tempY = e.Y;
if (tempY > int.Parse(boxY.Text))
{
bHeight.Text = (tempY - int.Parse(boxY.Text)).ToString();
}
else
{
bHeight.Text = (int.Parse(boxY.Text) - tempY).ToString();
}
surface.DrawRectangle(
pen1,
int.Parse(boxX.Text),
int.Parse(boxY.Text),
int.Parse(bWidth.Text),
int.Parse(bHeight.Text));
step = 1;
}
break;
}
So after I draw the images, I want to be able to select a figure and--for example--change the color or rotate it. But I cant seem to figure it out how to do it.
I suggest defining a base abstract shape class that has methods all shapes should provide, such as a method to draw itself on a graphics object, a method that says whether a point is within it / should could as selecting it, a method to rotate it by a given amount and a method to change the color.
Once you've got your shape class then you've got to work out how to fill in the methods for each derived shape. For drawing you've already got the code. For selecting it, that will be dependent on the shape. For something like a circle it's fairly easy, just calculate the distance between the center of the circle, and the point clicked, for something like a line it's harder as you don't want the user to have to click it exactly.
That leaves rotating and changing the colour. Changing the colour is easy, just have a Color property on the Shape class, then when you draw your shapes, use that colour to create a brush or pen.
As for rotation, take a look at Graphics.RotateTransform.
public abstract class Shape
{
public Color Color { get; set; }
public float Rotation { get; set; }
public Point Position { get; set; }
public Shape(Color color, float rotation, Point position)
{
Color = color;
Rotation = rotation;
Position = position;
}
public void ChangeRotation(float amount)
{
Rotation += amount;
}
public abstract void Draw(Graphics graphics);
public abstract bool WithinBounds(Point point);
}
public class Circle : Shape
{
public float Radius { get; set; }
public Circle(Color color, float rotation, Point position)
:base(color, rotation, position)
{
}
public override void Draw(Graphics graphics)
{
}
public override bool WithinBounds(Point point)
{
if (Math.Sqrt(Math.Pow(point.X - Position.X, 2) + Math.Pow(point.Y - Position.Y, 2)) <= Radius)
return true;
else
return false;
// Note, if statement could be removed to become the below:
//return Math.Sqrt(Math.Pow(point.X - Position.X, 2) + Math.Pow(point.Y - Position.Y, 2)) <= Radius;
}
}
Have a look at the RotateTransform method of the Graphics object. There is a TranslateTransform method too.
I need to zoom in and out a canvas. But the zoom should always be centered to the center of the screen (not the canvas). And this doesnt work with my current code! It always zooms to the center of the canvas and not the current screen.
The canvas should also be moveable, but should not be aloweded to move away from it's borders.
This is what I have so far:
XAML
<Grid>
<Canvas HorizontalAlignment="Left" Name="canvas1" VerticalAlignment="Top">
</Canvas>
</Grid>
C#
// x & y arent the position, it's how many pixel the object should move to the left/right etc
public void setPosition(double x, double y)
{
Thickness t = new Thickness(canvas1.Margin.Left + x, canvas1.Margin.Top + y, 0, 0);
if (t.Left > 0)
{
t.Left = 0;
}
else if(t.Left < -(canvas1.Width - System.Windows.SystemParameters.PrimaryScreenWidth))
{
t.Left = -(canvas1.Width - System.Windows.SystemParameters.PrimaryScreenWidth);
}
if (t.Top > 0)
{
t.Top = 0;
}
else if (t.Top < -(canvas1.Height - System.Windows.SystemParameters.PrimaryScreenHeight))
{
t.Top = -(canvas1.Height - System.Windows.SystemParameters.PrimaryScreenHeight);
}
canvas1.Margin = t;
}
public void setZoom(double zoom)
{
double tempW = canvas1.Width;
double tempH = canvas1.Height;
canvas1.Width *= (1 + zoom);
canvas1.Height *= (1 + zoom);
Debug.WriteLine("tempW: " + tempW + " tempH: " + tempH + " canvas1.Width: " + canvas1.Width + " canvas1.Height: " + canvas1.Height);
setPosition((tempW - canvas1.Width) / 2, ((tempH - canvas1.Height) / 2));
}
And yes I know there is stuff like ScaleTransform but for some reason this is more performant.. dont ask me why!
Can someone help me please?
Here is my solution for Pan and Zoom, both works fine in my complex application (12000px x 12000px).
You need to have a Zoom dependency property (double) and a ScaleTransform property
private double m_dCurZoom = 1.0;
public double Zoom
{
get { return m_dCurZoom; }
set
{
double oldzoom = m_dCurZoom;
if (m_dCurZoom != value)
{
m_dCurZoom = value;
OnPropertyChanged("Zoom");
UpdateZoom(oldzoom);
}
}
}
private ScaleTransform m_transZoom;
public ScaleTransform TransZoom
{
get { return m_transZoom; }
}
private TranslateTransform m_transPan;
/// <summary>
/// Gets or sets the Panning in X axis.
/// </summary>
public double PanX
{
get { return m_transPan.X; }
set
{
if (m_transPan.X != value)
{
m_transPan.X = value;
ResizeElementContents();
}
}
}
/// <summary>
/// Gets or sets the Panning in Y axis.
/// </summary>
public double PanY
{
get { return m_transPan.Y; }
set
{
if (m_transPan.Y != value)
{
m_transPan.Y = value;
ResizeElementContents();
}
}
}
When you update the Zoom, you call this method to calculate the center point:
public void UpdateZoom(double oldzoom)
{
// Are we visible?
if (m_root == null)
return;
// Get parent of VirtualPanel
FrameworkElement parent = GetRootParent();
if (parent == null)
return;
// Center point of the window
Point ptCenter = new Point(parent.RenderSize.Width / 2, parent.RenderSize.Height / 2);
// Translate into canvas coordinates
ptCenter = m_root.TranslatePoint(ptCenter, m_canvas);
// Update the zoom
m_transZoom.ScaleX = m_dCurZoom;
m_transZoom.ScaleY = m_dCurZoom;
m_transPan.X -= ptCenter.X * m_dCurZoom - ptCenter.X * oldzoom;
m_transPan.Y -= ptCenter.Y * m_dCurZoom - ptCenter.Y * oldzoom;
ResizeElementContents();
OnPropertyChanged("Zoom");
}
/// <summary>
/// Calculate the transform for given zoom & region-to-display for the given root's render-size
/// </summary>
public bool ResizeElementContents()
{
FrameworkElement parent = GetRootParent();
if (parent == null || m_transPan == null)
return false;
// Calculate the total region of the root
Rect? region = WtoHelper.CalcElementRect(Root);
if (region == null || region.HasValue == false)
return false;
// Scale the region to get the actal size with the zoom transformation.
double rgnWid = region.Value.Width * m_dCurZoom;
double rgnHei = region.Value.Height * m_dCurZoom;
// Image fits within our window width?
if (parent.RenderSize.Width > rgnWid)
m_transPan.X = (parent.RenderSize.Width - rgnWid) / 2;
else
{
// Image needs to be resized
if (m_transPan.X < -(rgnWid - parent.RenderSize.Width))
m_transPan.X = -(rgnWid - parent.RenderSize.Width);
else if (m_transPan.X > 0)
m_transPan.X = 0;
}
// Image fits within our window height?
if (parent.RenderSize.Height > rgnHei)
m_transPan.Y = (parent.RenderSize.Height - rgnHei) / 2;
else
{
// Image needs to be resized
if (m_transPan.Y < -(rgnHei - parent.RenderSize.Height))
m_transPan.Y = -(rgnHei - parent.RenderSize.Height);
else if (m_transPan.Y > 0)
m_transPan.Y = 0;
}
return true;
}
I don't provide all the code but it's a base if you can understand what I wrote.
If you need more information (I know that this question is old, this is why I'm not detailing everything) let me know.