2

I have three PictureBox with the images of a Gear (picture in post).
When I hover on them, they rotate. I am using System.Drawing.Image.RotateFlip(RotateFlipType).
It looks like only the center of the gear rotates but the edges are static.

Image of a Gear

private void rotationTimer_Tick(object sender, EventArgs e)
{
    Image flipImage = pictureBox1.Image;
    flipImage.RotateFlip(RotateFlipType.Rotate90FlipXY);
    pictureBox1.Image = flipImage;
}

private void rotationTimer2_Tick(object sender, EventArgs e)
{
    Image flipImage = pictureBox2.Image;
    flipImage.RotateFlip(RotateFlipType.Rotate90FlipNone);
    pictureBox2.Image = flipImage;
}

private void rotationTimer3_Tick(object sender, EventArgs e)
{
    Image flipImage = pictureBox3.Image;
    flipImage.RotateFlip(RotateFlipType.Rotate270FlipXY);
    pictureBox3.Image = flipImage;
}

private void pictureBox1_MouseHover(object sender, EventArgs e)
{
    rotationTimer.Start();
    rotationTimer2.Start();
    rotationTimer3.Start();
} //etc...
Jimi
  • 29,621
  • 8
  • 43
  • 61
Smith
  • 481
  • 1
  • 7
  • 14
  • 3
    You may have noticed that your gear's external edges are symmetrical, while the central section is not. So, if you rotate 90° (or a multiple of it) a symmetrical shape (like, say, a square), what happens? You might want to take a look at the [Matrix.RotateAt()](https://learn.microsoft.com/en-us/dotnet/api/system.drawing.drawing2d.matrix.rotateat) method. Set a different rotation angle and paint the Bitmap using the `PictureBox.Paint()` event. – Jimi Dec 13 '18 at 13:47
  • Thanks for the answer. The square rotates without problems. I will try to use Matrix.RotateAt () method. – Smith Dec 13 '18 at 13:56
  • 2
    Yes, *the square rotates without problems*, but it doesn't look like it is, because it always shows its sides in the same position :) If you need a hand with the Matrix, let me know. – Jimi Dec 13 '18 at 13:59
  • For rotating gears also [see here](https://stackoverflow.com/questions/26450764/how-to-rotate-image-in-picture-box/26455088#26455088) – TaW Dec 13 '18 at 13:59
  • Could you demonstrate by my example how to use Matrix.RotateAt() method? – Smith Dec 13 '18 at 14:04
  • Google "graphics.rotatetransform" to find more example code. One important detail is that you must not rotate the image repeatedly, that significantly reduces image quality. You must keep the original image around. Doing this up front and storing the images in a `List` is the best way to take advantage of the PictureBox.Image property and to make it smooth. – Hans Passant Dec 13 '18 at 14:19

1 Answers1

2

Here's an example of a rotating image using the Matrix.RotateAt() method.
This is a pretty simple process:

  • Create a Bitmap object from an Image file (or a Project resource); note that the Bitmap is [Cloned][2]: this way, we are detaching it from the FileStream (GDI+ won't lock the file while in use). Remember to Dispose() of it when you're done with it (or the application closes)
  • define a rotation angle that fits the image shape
  • set a Timer interval that generates the rotation speed (combined with the rotation angle). We are using a System.Windows.Form.Timer of course: we want it to tick in the UI thread (note that this object needs to be Disposed, too)
  • when the Timer ticks, Invalidate() the canvas (a PictureBox control, here)
  • use Matrix.RotateAt(gearCurrentRotationAngle, [ImageCentre]) and apply the Matrix to the Graphics geometric world transformation using its Transform property
  • add the chosen rotation angle to the current rotation angle at each rotation. When it reaches 360 degrees, re-set to min value (the gearRotationAngle field value, here)

Some other examples here:
Transparent Overlapping Circular Progress Bars
GraphicsPath and Matrix classes

Rotation Matrix


using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;

Bitmap gear = null;
RectangleF gearRect = Rectangle.Empty;
int gearRotateSpeed = 100;
int gearRotationAngle = 24;
int gearCurrentRotationAngle = 0;

System.Windows.Forms.Timer gearTimer = new System.Windows.Forms.Timer();

public Form1() {
    InitializeComponent();
    gear = Image.FromStream(new MemoryStream(File.ReadAllBytes(@"File Path")));

    // Assuming the Gear Image is square shaped and the PictureBox size remains constant
    // otherwise, recalculate in the Control.Resize event
    var gearScale = Math.Min(pictureBox1.Width, pictureBox1.Height) / (float)Gear.Width;
    var gearSize = new SizeF(gear.Width * gearScale, gear.Height * gearScale);
    gearRect = new RectangleF(new PointF((pictureBox1.Width - gearSize.Width) / 2.0f, (pictureBox1.Height - gearSize.Height) / 2.0f), gearSize);

    gearTimer.Tick += (s, e) => {
        gearCurrentRotationAngle += gearRotationAngle;
        if (gearCurrentRotationAngle > 360) gearCurrentRotationAngle = gearRotationAngle;
        pictureBox1.Invalidate();
    }
}

private void pictureBox1_MouseEnter(object sender, EventArgs e) {
    gearTimer.Interval = gearRotateSpeed;
    gearTimer.Start();
}

private void pictureBox1_MouseLeave(object sender, EventArgs e) => gearTimer.Stop();

private void pictureBox1_Paint(object sender, PaintEventArgs e) {
    var canvas = sender as PictureBox;
    e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
    e.Graphics.PixelOffsetMode = PixelOffsetMode.Half;

    PointF centerImage = new PointF(canvas.Width / 2, canvas.Height / 2);
    using (var mx = new Matrix()) {
        mx.RotateAt(gearCurrentRotationAngle, centerImage);
        e.Graphics.Transform = mx;
        e.Graphics.DrawImage(gear, gearRect);
    }
}
Jimi
  • 29,621
  • 8
  • 43
  • 61
  • Thank you very much! Unfortunately, when I paste the code, underlines with red SmoothingMode, PixelOffsetMode, Matrix. – Smith Dec 13 '18 at 15:05
  • @Smith Well, you need to add a `using` directive: `using System.Drawing.Drawing2D;` – Jimi Dec 13 '18 at 15:07
  • The two lines of code that change the value of `gearCurrentRotationAngle` should be in the `Tick` event, not the `Paint` event. As is, it's possible to get a rotation even when the mouse isn't within the PB... – Idle_Mind Jan 08 '21 at 22:47
  • @Idle_Mind Absolutely. The Image should also be positioned in the center of the canvas. This code was assuming a canvas size equal to the Image size. Updated. Thanks for noticing (one of my first posts :) – Jimi Jan 09 '21 at 02:12