My requirement is, want to zoom and pan the image programmatically.
Zoom-in and zoom-out programmatically using button click
Panning the image using touch interaction.
For the above requirement, I am facing the issue on maintaining panning state.
Steps to reproduce:
Zoom the image using button click.
Pan the image using touch move.
Now zoom-in or zoom-out using button click
Issue: Panning state is not maintained and image is zoomed from the over all center position of image.
Expected behavior: Image should be zoomed from center of the visual bounds area and panning state should be maintained.
I have attached my demo sample here
Here is my code [C#]:
MatrixTransform ZoomMatrixTransform;
public MainWindow()
{
InitializeComponent();
grid.DataContext = this;
updateZoom = true;
}
bool updateZoom;
public int ZoomFactor
{
get => (int)GetValue(ZoomFactorProperty);
set => SetValue(ZoomFactorProperty, value);
}
// Using a DependencyProperty as the backing store for ZoomFactor. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ZoomFactorProperty =
DependencyProperty.Register("ZoomFactor", typeof(int), typeof(MainWindow), new PropertyMetadata(100, OnZoomFactorChanged));
private static void OnZoomFactorChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var window = d as MainWindow;
int factor = (int)e.NewValue;
if (factor >= 50 && factor <= 400 && window.updateZoom)
{
window.PerformZoom(e.NewValue);
}
}
private void PerformZoom(object newValue)
{
float zoomValue = Convert.ToInt32(newValue) / 100f;
Matrix matrix = Matrix.Identity;
var scaleX = zoomValue;
var scaleY = zoomValue;
matrix.Scale(scaleX, scaleY);
ZoomMatrixTransform = new MatrixTransform(matrix);
foreach (UIElement child in panel.Children)
{
child.RenderTransformOrigin = new Point(0.5, 0.5);
child.RenderTransform = ZoomMatrixTransform;
}
}
private void Button_Click(object sender, RoutedEventArgs e) => ZoomFactor += 10;
private void Button_Click_1(object sender, RoutedEventArgs e) => ZoomFactor -= 10;
private void Panel_MouseWheel(object sender, MouseWheelEventArgs e)
{
var element = sender as UIElement;
var position = e.GetPosition(panel.Children[0]);
var matrix = ZoomMatrixTransform == null
? Matrix.Identity
: ZoomMatrixTransform.Matrix;
var scale = e.Delta >= 0 ? 1.1 : (1.0 / 1.1);
matrix.ScaleAtPrepend(scale, scale, position.X, position.Y);
var factor = (int)(matrix.M11 * 100);
updateZoom = false;
ZoomFactor = factor <= 50 ? 50 : factor >= 400 ? 400 : factor;
updateZoom = true;
if (factor >= 50 && factor <= 400)
{
ZoomMatrixTransform = new MatrixTransform(matrix);
foreach (UIElement child in panel.Children)
{
child.RenderTransform = ZoomMatrixTransform;
}
}
}
Point start;
Point origin;
private void Panel_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (ZoomMatrixTransform != null && ZoomMatrixTransform.Matrix != Matrix.Identity)
{
start = e.GetPosition(panel);
origin = new Point(ZoomMatrixTransform.Matrix.OffsetX,
ZoomMatrixTransform.Matrix.OffsetY);
editorImage.CaptureMouse();
}
Point position;
if (this.editorImage != null)
{
position = e.GetPosition(this.editorImage);
}
else
{
position = e.GetPosition(panel);
}
}
private void Panel_MouseMove(object sender, MouseEventArgs e)
{
Point position;
if (this.editorImage != null)
{
position = e.GetPosition(this.editorImage);
}
else
{
position = e.GetPosition(this.panel);
}
//CheckValueIsInRange(position);
if (editorImage.IsMouseCaptured && ZoomMatrixTransform.Matrix != Matrix.Identity)
{
FrameworkElement frameworkElement;
if (editorImage != null)
{
frameworkElement = editorImage;
}
else
{
frameworkElement = panel;
}
var elementBounds = new Rect(frameworkElement.RenderSize);
var transformedBounds = editorImage.TransformToAncestor(panel).TransformBounds(elementBounds);
var matrix = ZoomMatrixTransform.Matrix;
Vector vector = start - e.GetPosition(panel);
if (transformedBounds.Left < 0 && vector.X <= 0)
{
matrix.OffsetX = origin.X - vector.X;
}
else if (vector.X >= 0 && transformedBounds.Right >= panel.ActualWidth)
{
matrix.OffsetX = origin.X - vector.X;
}
if (transformedBounds.Top < 0 && vector.Y <= 0)
{
matrix.OffsetY = origin.Y - vector.Y;
}
else if (vector.Y >= 0 && transformedBounds.Bottom >= panel.ActualHeight)
{
matrix.OffsetY = origin.Y - vector.Y;
}
ZoomMatrixTransform.Matrix = matrix;
foreach (UIElement child in panel.Children)
{
child.RenderTransform = ZoomMatrixTransform;
}
}
}
private void Panel_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
=> editorImage.ReleaseMouseCapture();
private void Button_Click_2(object sender, RoutedEventArgs e)
{
ZoomMatrixTransform.Matrix = Matrix.Identity;
foreach (UIElement child in panel.Children)
{
child.RenderTransform = ZoomMatrixTransform;
}
}
I have tried different approach to maintain the panning state, but it is not working. Please share your idea on this.
I'm looking for the method for moving and changing the size of shape using mouse #C.
That shape made by mouse location (Point start, end) on pictureBox named "captureDesign".
I wanted to ask for a little bit of help.
I searched many similar case of question, but I couldn't solve it yet.
IF possible, please let me know to how.
Here is my code.
It's not everything, for example, I omitted the contents about the mode selection for shape using Button_click.
I studied the similar case.
But I haven't noticed it yet.
How can I associate startPt (#MouseDown) and endPt (#MouseUp) with MyMove to make the move successful?
MyMove code is written in upper link. I need it change.
Actually I need to code for change the size but, first of all, I want to move that using mouse.
namespace Pilot
{
enum DrawMode { LINE, RECTANGLE, CIRCLE, NUMBER };
public partial class mainForm : Form
{
#region define
private bool _isCaptionShow = false;
private ScreenPicture sp;
private IContainer components = null;
Bitmap bitmap;
private DrawMode drawMode;
private Graphics g;
private Pen pen = new Pen(Color.Red, 7);
Point startPt, endPt, currPt, prevPt, addPt;
private int numberCount = 0;
int rectWidth, rectHeight;
Font font = new Font("Arial", 12);
private bool selectMode = false;
private void selectModeButton_CheckedChanged(object sender, EventArgs e)
{
if (selectModeButton.Checked == true)
selectMode = true;
else
selectMode = false;
}
MyMove m;
Point deltaStart;
Point deltaEnd;
bool dragging = false;
#region Contents on PictureBox "captureDesign;" when mouse clicked.
private void captureDesign_MouseDown(object sender, MouseEventArgs e)
{
startPt = new Point(e.X, e.Y);
prevPt = startPt;
currPt = startPt;
if (selectMode)
{
if (e.Button == MouseButtons.Left && m.IsPointOnLine(e.Location, 5))
{
dragging = true;
deltaStart = new Point(startPt.X- e.Location.X, startPt.Y - e.Location.Y);
}
}
}
#region Contents on PictureBox captureDesign when Mouse dropped.
private void captureDesign_MouseUp(object sender, MouseEventArgs e)
{
g = captureDesign.CreateGraphics();
endPt = new Point(e.X, e.Y);
m = new MyMove(pen, startPt, endPt);
#region calculate between start Point ~ end Point to width, height
if (endPt.X < startPt.X)
{
rectWidth = Math.Abs(endPt.X - startPt.X);
addPt.X = endPt.X;
}
else
{
rectWidth = Math.Abs(endPt.X - startPt.X);
addPt.X = startPt.X;
}
if (endPt.Y < startPt.Y)
{
rectHeight = Math.Abs(endPt.Y - startPt.Y);
addPt.Y = endPt.Y;
}
else
{
rectHeight = Math.Abs(endPt.Y - startPt.Y);
addPt.Y = startPt.Y;
}
#endregion
if (selectMode)
{
deltaEnd = new Point(endPt.X - e.Location.X, endPt.Y - e.Location.Y);
}
else //No selectMode
{
#region draw the shape in case of drawMode
switch (drawMode)
{
case DrawMode.LINE:
if (arrowCheck.Checked == true)
{
pen.StartCap = LineCap.ArrowAnchor;
}
else
//g.DrawLine(pen, startPt, endPt);
g.DrawLine(m.mpen, m.mStart, m.mEnd);
break;
case DrawMode.RECTANGLE:
//g.DrawRectangle(pen, new Rectangle(startPt, new Size(endPt.X - startPt.X, endPt.Y - startPt.Y)));
g.DrawRectangle(pen, new Rectangle(addPt, new Size(rectWidth, rectHeight)));
break;
case DrawMode.CIRCLE:
g.DrawEllipse(pen, new Rectangle(addPt, new Size(rectWidth, rectHeight)));
break;
case DrawMode.NUMBER:
numberCount++;
g.DrawString(numberCount.ToString(), font, Brushes.White, endPt);
break;
}
#endregion
}
}
#region
private void captureDesign_MouseMove(object sender, MouseEventArgs e)
{
if (dragging && deltaStart != null && deltaEnd != null)
{
m.mStart = new Point(deltaStart.X + e.Location.X, deltaStart.Y + e.Location.Y);
m.mEnd = new Point(deltaEnd.X + e.Location.X, deltaEnd.Y + e.Location.Y);
}
}
}
public class MyMove
{
public Pen mpen { get; set; }
public Point mStart { get; set; }
public Point mEnd { get; set; }
public MyMove(Pen p, Point p1, Point p2)
{
mpen = p;
mStart = p1;
mEnd = p2;
}
public float slope
{
get
{
return (((float)mEnd.Y - (float)mStart.Y) / ((float)mEnd.X - (float)mStart.X));
}
}
public float YIntercept
{
get
{
return mStart.Y - slope * mStart.X;
}
}
public bool IsPointOnLine(Point p, int cushion)
{
float temp = (slope * p.X + YIntercept);
if (temp >= (p.Y-cushion) && temp <=(p.Y+cushion))
{
return true;
}
else
{
return false;
}
}
}
1ST ANSWER
Everything happens on MouseMove(object sender, MouseEventArgs e).
Here is a sample
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
//If we are not allowed to draw, simply return and disregard the rest of the code
if (!_canDraw) return;
//The x-value of our rectangle should be the minimum between the start x-value and the current x-position
int x = Math.Min(_startX, e.X);
//The y-value of our rectangle should also be the minimum between the start y-value and current y-value
int y = Math.Min(_startY, e.Y);
//The width of our rectangle should be the maximum between the start x-position and current x-position minus
//the minimum of start x-position and current x-position
int width = Math.Max(_startX, e.X) - Math.Min(_startX, e.X);
//For the hight value, it's basically the same thing as above, but now with the y-values:
int height = Math.Max(_startY, e.Y) - Math.Min(_startY, e.Y);
_rect = new Rectangle(x, y, width, height);
//Refresh the form and draw the rectangle
Refresh();
}
protected override void OnPaint(PaintEventArgs e)
{
//Create a new 'pen' to draw our rectangle with, give it the color red and a width of 2
using (Pen pen = new Pen(Color.Red, 2))
{
//Draw the rectangle on our form with the pen
e.Graphics.DrawRectangle(pen, _rect);
}
}
You can further study the detail here, C# Tutorial - Drawing rectangles with the mouse.
2ND ANSWER (UPDATE)
I achieve the solution you want but I use a third party library MoveGraphLibrary. You can further read this article Moveable Resizable Objects.
Sample Code
GRAPHICAL OBJECT
public class Rectangle : GraphicalObject
{
protected RectangleF rc;
protected Resizing resize;
protected float wMin, wMax, hMin, hMax;
protected int radius;
protected int halfstrip;
protected SolidBrush brush;
int minsize = 25;
// -------------------------------------------------
public Rectangle(RectangleF rect, RectRange range, int rad, int half, Color color)
{
rc = new RectangleF(rect.X, rect.Y, Math.Max(minsize, rect.Width), Math.Max(minsize, rect.Height));
if (range == null)
{
wMin = wMax = rc.Width;
hMin = hMax = rc.Height;
}
else
{
wMin = Math.Max(minsize, Math.Min(rc.Width, range.MinWidth));
wMax = Math.Max(rc.Width, range.MaxWidth);
hMin = Math.Max(minsize, Math.Min(rc.Height, range.MinHeight));
hMax = Math.Max(rc.Height, range.MaxHeight);
}
RectRange realrange = new RectRange(wMin, wMax, hMin, hMax);
resize = realrange.Resizing;
radius = rad;
halfstrip = half;
brush = new SolidBrush(color);
}
// -------------------------------------------------
// ------------------------------------------------- RectAround
new public RectangleF RectAround
{
get { return (rc); }
}
// ------------------------------------------------- Radius
public int Radius
{
get { return (radius); }
set
{
radius = Math.Abs(value);
DefineCover();
}
}
// ------------------------------------------------- HalfStrip
public int HalfStrip
{
get { return (halfstrip); }
set
{
halfstrip = Math.Abs(value);
DefineCover();
}
}
// ------------------------------------------------- Color
public Color Color
{
get { return (brush.Color); }
set { brush.Color = value; }
}
// -------------------------------------------------
public void Draw(Graphics grfx)
{
grfx.FillRectangle(brush, rc);
}
// ------------------------------------------------- Resizing
public Resizing Resizing
{
get { return (resize); }
set
{
resize = value;
DefineCover();
}
}
// ------------------------------------------------- DefineCover
public override void DefineCover()
{
cover = new Cover(rc, resize, radius, halfstrip);
}
// -------------------------------------------------
public override void Move(int dx, int dy)
{
rc.X += dx;
rc.Y += dy;
}
// ------------------------------------------------- MoveNode
public override bool MoveNode(int iNode, int dx, int dy, Point ptM, MouseButtons catcher)
{
bool bRet = false;
if (catcher == MouseButtons.Left)
{
float wNew, hNew;
switch (resize)
{
case Resizing.Any:
if (iNode == 8)
{
Move(dx, dy);
}
else if (iNode == 0) //LT corner
{
hNew = rc.Height - dy;
if (hMin <= hNew && hNew <= hMax)
{
MoveBorder_Top(dy);
bRet = true;
}
wNew = rc.Width - dx;
if (wMin <= wNew && wNew <= wMax)
{
MoveBorder_Left(dx);
bRet = true;
}
}
else if (iNode == 1) // RT corner
{
hNew = rc.Height - dy;
if (hMin <= hNew && hNew <= hMax)
{
MoveBorder_Top(dy);
bRet = true;
}
wNew = rc.Width + dx;
if (wMin <= wNew && wNew <= wMax)
{
MoveBorder_Right(dx);
bRet = true;
}
}
else if (iNode == 2) // RB corner
{
wNew = rc.Width + dx;
if (wMin <= wNew && wNew <= wMax)
{
MoveBorder_Right(dx);
bRet = true;
}
hNew = rc.Height + dy;
if (hMin <= hNew && hNew <= hMax)
{
MoveBorder_Bottom(dy);
bRet = true;
}
}
else if (iNode == 3) // LB corner
{
hNew = rc.Height + dy;
if (hMin <= hNew && hNew <= hMax)
{
MoveBorder_Bottom(dy);
bRet = true;
}
wNew = rc.Width - dx;
if (wMin <= wNew && wNew <= wMax)
{
MoveBorder_Left(dx);
bRet = true;
}
}
else if (iNode == 4) // on left side
{
wNew = rc.Width - dx;
if (wMin <= wNew && wNew <= wMax)
{
MoveBorder_Left(dx);
bRet = true;
}
}
else if (iNode == 5) // on right side
{
wNew = rc.Width + dx;
if (wMin <= wNew && wNew <= wMax)
{
MoveBorder_Right(dx);
bRet = true;
}
}
else if (iNode == 6) // on top
{
hNew = rc.Height - dy;
if (hMin <= hNew && hNew <= hMax)
{
MoveBorder_Top(dy);
bRet = true;
}
}
else if (iNode == 7) // on bottom
{
hNew = rc.Height + dy;
if (hMin <= hNew && hNew <= hMax)
{
MoveBorder_Bottom(dy);
bRet = true;
}
}
break;
case Resizing.NS:
if (iNode == 2)
{
Move(dx, dy);
}
else if (iNode == 0) // on top
{
hNew = rc.Height - dy;
if (hMin <= hNew && hNew <= hMax)
{
MoveBorder_Top(dy);
bRet = true;
}
}
else if (iNode == 1) // on bottom
{
hNew = rc.Height + dy;
if (hMin <= hNew && hNew <= hMax)
{
MoveBorder_Bottom(dy);
bRet = true;
}
}
break;
case Resizing.WE:
if (iNode == 2)
{
Move(dx, dy);
}
else if (iNode == 0) // on left side
{
wNew = rc.Width - dx;
if (wMin <= wNew && wNew <= wMax)
{
MoveBorder_Left(dx);
bRet = true;
}
}
else if (iNode == 1) // on right side
{
wNew = rc.Width + dx;
if (wMin <= wNew && wNew <= wMax)
{
MoveBorder_Right(dx);
bRet = true;
}
}
break;
case Resizing.None:
Move(dx, dy);
break;
}
}
return (bRet);
}
// ------------------------------------------------- MoveBorder_Top
private void MoveBorder_Top(int dy)
{
rc.Y += dy;
rc.Height -= dy;
}
// ------------------------------------------------- MoveBorder_Bottom
private void MoveBorder_Bottom(int dy)
{
rc.Height += dy;
}
// ------------------------------------------------- MoveBorder_Left
private void MoveBorder_Left(int dx)
{
rc.X += dx;
rc.Width -= dx;
}
// ------------------------------------------------- MoveBorder_Right
private void MoveBorder_Right(int dx)
{
rc.Width += dx;
}
public PointF Location
{
get
{
return rc.Location;
}
}
}
MAIN FORM
public partial class Form1 : Form
{
RectRange rr;
// Variables use for Moving & Resizing
NumericUpDown numericUD_Radius = new NumericUpDown();
NumericUpDown numericUD_HalfStrip = new NumericUpDown();
string[] strs = new string[] {"Circles' radius",
"Half strip width"
};
Mover mover;
Point ptMouse_Down;
bool bShowCovers = false;
RigidlyBoundRectangles rigidrectsView;
List<Shapes.Rectangle> rects = new List<Shapes.Rectangle>();
int radius, halfstrip;
// Variables use for Drawing
bool isMouseDown = false;
public Form1()
{
InitializeComponent();
lblXAxis.Text = $"X Axis: -";
lblYAxis.Text = $"Y Axis: -";
numericUD_Radius.Value = 6;
numericUD_HalfStrip.Value = 3;
mover = new Mover(panel1);
SizeF[] sizefStrs = Auxi_Geometry.MeasureStrings(this, strs);
rigidrectsView = new RigidlyBoundRectangles(new Control[] { numericUD_Radius, numericUD_HalfStrip });
rigidrectsView.Add(Auxi_Geometry.RectangleToRectangleSide(numericUD_Radius.Bounds, Side.E, sizefStrs[0], 4), "Radius");
rigidrectsView.Add(Auxi_Geometry.RectangleToRectangleSide(numericUD_HalfStrip.Bounds, Side.E, sizefStrs[1], 4), "Strip");
rigidrectsView.AddUnionRectangle();
radius = Convert.ToInt32(numericUD_Radius.Value);
halfstrip = Convert.ToInt32(numericUD_HalfStrip.Value);
rr = new RectRange(panel1.MinimumSize.Width, panel1.Size.Width, panel1.MinimumSize.Height, panel1.Size.Height);
rects.Add(new Shapes.Rectangle(new RectangleF(100, 100, 300, 400), rr, radius, halfstrip, Color.Black));
RenewMover();
}
private void RenewMover()
{
mover.Clear();
mover.Insert(0, rigidrectsView);
for (int i = 0; i < rects.Count; i++)
{
mover.Add(rects[i]);
}
}
private void panel1_Paint(object sender, PaintEventArgs e)
{
Graphics grfx = e.Graphics;
GraphicalObject grobj;
for (int i = mover.Count - 1; i >= 0; i--)
{
grobj = mover[i].Source;
if (grobj is Shapes.Rectangle)
{
(grobj as Shapes.Rectangle).Draw(grfx);
}
if (bShowCovers)
{
mover[i].DrawCover(grfx);
}
}
}
private void panel1_MouseMove(object sender, MouseEventArgs e)
{
lblXAxis.Text = $"X Axis: {e.X}";
lblYAxis.Text = $"Y Axis: {e.Y}";
if (rbMoveOrResize.Checked && mover.Move(e.Location))
{
panel1.Invalidate();
}
else
{
if (isMouseDown)
{
var rectangle = rects.Last();
var drawRectangle = new Shapes.Rectangle(new RectangleF(rectangle.Location.X,
rectangle.Location.Y,
e.X - rectangle.Location.X,
e.Y - rectangle.Location.Y),
rr, radius, halfstrip, Color.Black);
rects.Remove(rects.Last());
rects.Add(drawRectangle);
RenewMover();
panel1.Invalidate();
}
}
}
private void panel1_MouseLeave(object sender, EventArgs e)
{
lblXAxis.Text = $"X Axis: -";
lblYAxis.Text = $"Y Axis: -";
}
private void panel1_MouseDown(object sender, MouseEventArgs e)
{
if (rbMoveOrResize.Checked)
{
ptMouse_Down = e.Location;
mover.Catch(e.Location, e.Button);
}
else
{
isMouseDown = true;
rects.Add(new Shapes.Rectangle(new RectangleF(e.Location.X, e.Location.Y, 0, 0), rr, radius, halfstrip, Color.Black));
}
}
private void panel1_MouseUp(object sender, MouseEventArgs e)
{
if (rbMoveOrResize.Checked && mover.Release())
{
if (e.Button == MouseButtons.Left &&
Auxi_Geometry.Distance(ptMouse_Down, e.Location) <= 3)
{
GraphicalObject grobj = mover[mover.ReleasedObject].Source;
if (grobj is Shapes.Rectangle)
{
PopupRectangle(grobj.ID);
}
}
}
else
{
isMouseDown = false;
}
}
private void rb_CheckedChanged(object sender, EventArgs e)
{
bShowCovers = rbMoveOrResize.Checked;
panel1.Invalidate();
}
private void PopupRectangle(long id)
{
for (int i = rects.Count - 1; i > 0; i--)
{
if (id == rects[i].ID)
{
Shapes.Rectangle elem = rects[i];
rects.RemoveAt(i);
rects.Insert(0, elem);
RenewMover();
panel1.Invalidate();
break;
}
}
}
}
SAMPLE OUTPUT
I'm developing my own image viewer and would like to pan and zoom the image. Currently using a modified version of "ZoomBorder" to achieve this, from this answer: https://stackoverflow.com/a/6782715/13646636.
There is the problem that the user can drag the image out of the screen/parent container, which is an unwanted behavior.
I've created a sample application to illustrate the issue, with the minimal needed code:
MainWindow.xaml
<Window
x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow"
SizeToContent="WidthAndHeight">
<Grid x:Name="ParentContainer">
<Border x:Name="MainBorderImage">
<Image
x:Name="MainImage"
Source="https://w.wallhaven.cc/full/ym/wallhaven-ympqxl.jpg"
Stretch="Fill" />
</Border>
</Grid>
</Window>
MainWindow.xaml.cs
using System;
using System.Linq;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
namespace WpfApp1
{
public partial class MainWindow : Window
{
private Point origin;
private Point start;
private ScaleTransform scaleTransform;
private TranslateTransform translateTransform;
private double aspectRatio;
public MainWindow()
{
InitializeComponent();
MainImage.MouseWheel += MainImage_MouseWheel;
MainImage.MouseLeftButtonDown += MainImage_MouseLeftButtonDown;
MainImage.MouseLeftButtonUp += MainImage_MouseLeftButtonUp;
MainImage.MouseMove += MainImage_MouseMove;
PreviewMouseRightButtonDown += delegate { Reset(); };
ContentRendered += delegate {
InitializeZoom();
Scale_Image();
};
}
private void Scale_Image()
{
double width = MainImage.Source.Width;
double height = MainImage.Source.Height;
double maxWidth = Math.Min(SystemParameters.PrimaryScreenWidth - 30, width);
double maxHeight = Math.Min(SystemParameters.PrimaryScreenHeight - 30, height);
aspectRatio = Math.Min(maxWidth / width, maxHeight / height);
width *= aspectRatio;
height *= aspectRatio;
MainImage.Width = width;
MainImage.Height = height;
}
private void InitializeZoom()
{
// Initialize transforms
MainImage.RenderTransform = new TransformGroup
{
Children = new TransformCollection {
new ScaleTransform(),
new TranslateTransform()
}
};
// Set transforms to UI elements
scaleTransform = (ScaleTransform)((TransformGroup)
MainImage.RenderTransform).Children.First(tr => tr is ScaleTransform);
translateTransform = (TranslateTransform)((TransformGroup)
MainImage.RenderTransform).Children.First(tr => tr is TranslateTransform);
}
private void Reset()
{
scaleTransform.ScaleX = 1.0;
scaleTransform.ScaleY = 1.0;
translateTransform.X = 0.0;
translateTransform.Y = 0.0;
}
private void MainImage_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
MainImage.ReleaseMouseCapture();
}
private void MainImage_MouseMove(object sender, MouseEventArgs e)
{
// Don't drag when full scale
// and don't drag it if mouse not held down on image
if (!MainImage.IsMouseCaptured || scaleTransform.ScaleX == 1)
{
return;
}
// Drag image by modifying X,Y coordinates
var dragMousePosition = start - e.GetPosition(this);
var newXproperty = origin.X - dragMousePosition.X;
var newYproperty = origin.Y - dragMousePosition.Y;
if (newXproperty < 0)
{
newXproperty = 0;
}
if (newYproperty < 0)
{
newYproperty = 0;
}
/// Top corners are 0, which is easy enough, but how to count
/// the bottom corners?
if (true) // Calculate to not go out of parent container
{
// Set max X property
}
if (true) // Calculate to not go out of parent container
{
// Set max Y property
}
translateTransform.X = newXproperty;
translateTransform.Y = newYproperty;
e.Handled = true;
}
private void MainImage_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
MainImage.CaptureMouse();
start = e.GetPosition(MainBorderImage);
origin = new Point(translateTransform.X, translateTransform.Y);
}
private void MainImage_MouseWheel(object sender, MouseWheelEventArgs e)
{
double zoom = e.Delta > 0 ? .2 : -.2;
if (!(e.Delta > 0) && (scaleTransform.ScaleX < .4 || scaleTransform.ScaleY < .4))
return;
Point relative = e.GetPosition(MainImage);
double absoluteX;
double absoluteY;
absoluteX = relative.X * scaleTransform.ScaleX + translateTransform.X;
absoluteY = relative.Y * scaleTransform.ScaleY + translateTransform.Y;
scaleTransform.ScaleX += zoom;
scaleTransform.ScaleY += zoom;
translateTransform.X = absoluteX - relative.X * scaleTransform.ScaleX;
translateTransform.Y = absoluteY - relative.Y * scaleTransform.ScaleY;
}
}
}
Try the code below, does it make sense for you.
private void MainImage_MouseMove(object sender, MouseEventArgs e)
{
// Don't drag when full scale and don't drag it if mouse not held down on image
if (!MainImage.IsMouseCaptured || scaleTransform.ScaleX == 1)
{
return;
}
// Drag image by modifying X,Y coordinates
var dragMousePosition = start - e.GetPosition(this);
var newXproperty = origin.X - dragMousePosition.X;
var newYproperty = origin.Y - dragMousePosition.Y;
var isXOutOfBorder = this.MainBorderImage.ActualWidth < (this.MainImage.ActualWidth * this.scaleTransform.ScaleX);
var isYOutOfBorder = this.MainBorderImage.ActualHeight < (this.MainImage.ActualHeight * this.scaleTransform.ScaleY);
if ((isXOutOfBorder && newXproperty> 0) || (!isXOutOfBorder && newXproperty < 0))
{
newXproperty = 0;
}
if((isYOutOfBorder && newYproperty > 0) || (!isYOutOfBorder && newYproperty < 0))
{
newYproperty = 0;
}
var maxX = this.MainBorderImage.ActualWidth - (this.MainImage.ActualWidth * this.scaleTransform.ScaleX);
if ((isXOutOfBorder && newXproperty < maxX) || (!isXOutOfBorder && newXproperty > maxX))
{
newXproperty = maxX;
}
var maxY = this.MainBorderImage.ActualHeight - (this.MainImage.ActualHeight * this.scaleTransform.ScaleY);
if ((isXOutOfBorder && newYproperty < maxY) || (!isXOutOfBorder && newYproperty > maxY))
{
newYproperty = maxY;
}
translateTransform.X = newXproperty;
translateTransform.Y = newYproperty;
e.Handled = true;
}
I am currently working on a topdown game made in windows forms. In the game, your character rotates after your mouse and you can shoot a shot from your character in the direction of your mouse. To create multiple areas in the game, I chose to use groupboxes as separate areas in the game that you can switch between using buttons but I've run in to a problem. I can no longer rotate or move my character or even shoot within the groupboxes.
I've tried setting breakpoints and discovered that the keyup, keydown and mousemove methods are not being called but I don't know why. For some reason I get an error when i release space in the btnSave_Click method which i marked in the code.
static Image originalImage;
bool pRight, pLeft, pUp, pDown;
string[] Saves;
Save[] RSaves = new Save[4];
Save yoursave;
Save temp;
int slot;
NewGame newgame;
SavedGames savedgames;
string savedata, name = "";
int lvl = 1;
double exp = 0, money = 0;
int pSpeed = 5;
double deltaY, deltaX;
float interval = 7;
Point start;
Point nextStart;
Point cursor;
float radian;
const double Rad2Grad = (180 / Math.PI);
public MainGame()
{
InitializeComponent();
pbPlayer.BringToFront();
gbxTown.AllowDrop = true;
gbxQ1.AllowDrop = true;
pbShot.Location = pbPlayer.Location;
// newgame = new NewGame();
// savedgames = new SavedGames();
originalImage = pbPlayer.Image;
}
public void setSaves(string savedata, int slot)
{
Saves = savedata.Split('#');
string a1 = Saves[0];
string a2 = Saves[1];
string a3 = Saves[2];
string a4 = Saves[3];
RSaves[0] = temp.StringToSaves(temp, a1);
RSaves[1] = temp.StringToSaves(temp, a2);
RSaves[2] = temp.StringToSaves(temp, a3);
RSaves[3] = temp.StringToSaves(temp, a4);
yoursave = RSaves[slot - 1];
name = yoursave.getName();
this.slot = slot;
Controls.Add(pbPlayer);
Controls.Add(pbShot);
}
private void MainGame_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyData == Keys.A)
{
pRight = false;
pLeft = true;
}
else if (e.KeyData == Keys.D)
{
pRight = true;
pLeft = false;
}
else if (e.KeyData == Keys.S)
{
pUp = true;
pDown = false;
}
else if (e.KeyData == Keys.W)
{
pUp = false;
pDown = true;
}
if (e.KeyData == Keys.Space)
{
start = pbPlayer.Location;
cursor = Cursor.Position;
nextStart = start;
deltaY = cursor.Y - start.Y;
deltaX = cursor.X - start.X;
double test = Angle(start, cursor);
radian = (float)(Angle(start, cursor) - 175);
timer2.Enabled = true;
}
}
private void MainGame_KeyUp_1(object sender, KeyEventArgs e)
{
if (e.KeyData == Keys.A)
{
pLeft = false;
}
else if (e.KeyData == Keys.D)
{
pRight = false;
}
else if (e.KeyData == Keys.S)
{
pUp = false;
}
else if (e.KeyData == Keys.W)
{
pDown = false;
}
if (e.KeyData == Keys.Space)
{
timer2.Stop();
pbShot.Location = start;
}
}
private void timer1_Tick(object sender, EventArgs e)
{
MovePlayer();
//checkCollision();
}
private void btnNextArea_Click(object sender, EventArgs e)
{
pbPlayer.Parent = gbxQ1;
pbShot.Parent = gbxQ1;
gbxQ1.Location = new Point(0, 0);
gbxTown.Location = new Point(605, 0);
pbPlayer.Location = new Point(100, 200);
pbShot.Location = pbPlayer.Location;
}
private void btnSave_Click(object sender, EventArgs e)
{
newgame = new NewGame();
savedgames = new SavedGames();
RSaves[slot - 1] = new Save(name, money, lvl, exp);
for (int i = 0; i < 4; i++) //When the Spacebar is released, a System.IndexOutOfRangeException error happens for i.
{
Saves[i] = RSaves[i].SavesToString();
}
savedata = Saves[0] + Saves[1] + Saves[2] + Saves[3];
System.IO.File.WriteAllLines(#"saves.txt", Saves);
newgame.setSaves(savedata);
savedgames.setSaves(savedata);
}
private void btnBack_Click(object sender, EventArgs e)
{
pbPlayer.Parent = gbxTown;
pbShot.Parent = gbxTown;
gbxTown.Location = new Point(0, 0);
gbxQ1.Location = new Point(605, 0);
pbPlayer.Location = new Point(100, 200);
pbShot.Location = pbPlayer.Location;
}
private void MainGame_MouseMove_1(object sender, MouseEventArgs e)
{
var y2 = e.Y;
var y1 = (this.pbPlayer.Location.Y + (this.pbPlayer.Height / 2));
var x2 = e.X;
var x1 = (this.pbPlayer.Location.X + (this.pbPlayer.Width / 2));
var angle = (float)Math.Atan2((y1 - y2), (x1 - x2));
pbPlayer.Image = RotateImage(originalImage, (angle * 57));
}
private double Angle(Point start, Point end)
{
return (float)Math.Atan2(end.Y - start.Y, end.X - start.X) * 57;
}
private void timer2_Tick_1(object sender, EventArgs e)
{
nextStart.X -= Convert.ToInt16(interval * ((float)Math.Cos(radian / Rad2Grad)));
nextStart.Y -= Convert.ToInt16(interval * ((float)Math.Sin(radian / Rad2Grad)));
pbShot.Location = nextStart;
}
public static Image RotateImage(Image img, float rotationAngle)
{
Bitmap bmp = new Bitmap(img.Width, img.Height);
Graphics gfx = Graphics.FromImage(bmp);
gfx.TranslateTransform((float)bmp.Width / 2, (float)bmp.Height / 2);
gfx.RotateTransform(rotationAngle);
gfx.TranslateTransform(-(float)bmp.Width / 2, -(float)bmp.Height / 2);
gfx.InterpolationMode = InterpolationMode.HighQualityBicubic;
gfx.DrawImage(img, new Point(0, 0));
gfx.Dispose();
return bmp;
}
private void MovePlayer()
{
if (pRight == true && pbPlayer.Left < 900)
{
pbPlayer.Left += pSpeed;
}
else if (pLeft == true && pbPlayer.Left > 0)
{
pbPlayer.Left -= pSpeed;
}
else if (pUp == true && pbPlayer.Top < 600)
{
pbPlayer.Top += pSpeed;
}
else if (pDown == true && pbPlayer.Top > 0)
{
pbPlayer.Top -= pSpeed;
}
}
The expected outcome is for the movement, aiming and firing to work as intended within the groupboxes but currently they do not execute in the code while in the groupboxes. It works fine in the form but not in the groupboxes. I would appriciate any help.
Im trying to use a shaking motion to clear the content of an inkpresenter, however shaking is just crashing my application. Here is the code:
public class ShakeDetect
{
private Accelerometer _accelerometer = null;
object SyncRoot = new object();
private int _minimumShakes;
ShakeRecord[] _shakeRecordList;
private int _shakeRecordIndex = 0;
private const double MinimumAccelerationMagnitude = 1.2;
private const double MinimumAccelerationMagnitudeSquared = MinimumAccelerationMagnitude * MinimumAccelerationMagnitude;
private static readonly TimeSpan MinimumShakeTime = TimeSpan.FromMilliseconds(500);
public event EventHandler<EventArgs> ShakeEvent = null;
protected void OnShakeEvent()
{
if (ShakeEvent != null)
{
ShakeEvent(this, new EventArgs());
}
}
[Flags]
public enum Direction
{
None = 0,
North = 1,
South = 2,
East = 8,
West = 4,
NorthWest = North | West,
SouthWest = South | West,
SouthEast = South | East,
NorthEast = North | East
} ;
public struct ShakeRecord
{
public Direction ShakeDirection;
public DateTime EventTime;
}
public ShakeDetect()
: this(2)
{
}
public ShakeDetect(int minShakes)
{
_minimumShakes = minShakes;
_shakeRecordList = new ShakeRecord[minShakes];
}
public void Start()
{
lock (SyncRoot)
{
if (_accelerometer == null)
{
_accelerometer = new Accelerometer();
_accelerometer.ReadingChanged += new EventHandler<AccelerometerReadingEventArgs>(_accelerometer_ReadingChanged);
_accelerometer.Start();
}
}
}
public void Stop()
{
lock (SyncRoot)
{
if (_accelerometer != null)
{
_accelerometer.Stop();
_accelerometer.ReadingChanged -= _accelerometer_ReadingChanged;
_accelerometer = null;
}
}
}
Direction DegreesToDirection(double direction)
{
if ((direction >= 337.5) || (direction <= 22.5))
return Direction.North;
if ((direction <= 67.5))
return Direction.NorthEast;
if (direction <= 112.5)
return Direction.East;
if (direction <= 157.5)
return Direction.SouthEast;
if (direction <= 202.5)
return Direction.South;
if (direction <= 247.5)
return Direction.SouthWest;
if (direction <= 292.5)
return Direction.West;
return Direction.NorthWest;
}
void CheckForShakes()
{
int startIndex = (_shakeRecordIndex - 1);
if (startIndex < 0) startIndex = _minimumShakes - 1;
int endIndex = _shakeRecordIndex;
if ((_shakeRecordList[endIndex].EventTime.Subtract(_shakeRecordList[startIndex].EventTime)) <= MinimumShakeTime)
{
OnShakeEvent();
}
}
void _accelerometer_ReadingChanged(object sender, AccelerometerReadingEventArgs e)
{
if ((e.X * e.X + e.Y * e.Y) > MinimumAccelerationMagnitudeSquared)
{
double degrees = 180.0 * Math.Atan2(e.Y, e.X) / Math.PI;
Direction direction = DegreesToDirection(degrees);
if ((direction & _shakeRecordList[_shakeRecordIndex].ShakeDirection) != Direction.None)
return;
ShakeRecord record = new ShakeRecord();
record.EventTime = DateTime.Now;
record.ShakeDirection = direction;
_shakeRecordIndex = (_shakeRecordIndex + 1) % _minimumShakes;
_shakeRecordList[_shakeRecordIndex] = record;
CheckForShakes();
}
}
}
And then in my app I use:
private ShakeDetect _shakeDecetor;
// Constructor
public MainPage()
{
InitializeComponent();
_shakeDecetor = new ShakeDetect();
_shakeDecetor.ShakeEvent += new EventHandler<EventArgs>(_shakeDetect_ShakeEvent);
_shakeDecetor.Start();
Finally when the shake is detected:
void _shakeDetect_ShakeEvent(object sender, EventArgs e)
{
CanvasIP.Strokes.Clear();
CanvasIP.Background = new SolidColorBrush(Colors.Black);
}
I'm guessing that the problem is somewhere in my ShakeDetect class, or in the ShakeEvent.
Ok So from Hans' advice my code should be:
void _shakeDetect_ShakeEvent(object sender, EventArgs e)
{
this.Dispatcher.BeginInvoke(() =>
{
CanvasIP.Strokes.Clear();
CanvasIP.Background = new SolidColorBrush(Colors.Black);
});
}
Is this correct now?
The ReadingChanged event is raised on another thread. You can not touch UI components directly, you must marshal a call back to the main thread. Use Dispatcher.BeginInvoke(), it is well described in this MSDN library article.