How to scroll an zoomed image C# - c#

I have a picturebox and i can zoom it.
When i zoom the picturebox i need to scroll left or right the image.
Can you tell me why when i zoom the image the left side is fixed and i can't scroll on left?
I have activated the AutoScroll on the main panel, but i can scroll only to right, but my image is centered and i need to scoll also to left.
What i want is like when you zoom and scroll an image inside a Photo app in Win 10. But i'm using the scoll bar and not the mouse pointer.
The structure is like in the next image, in red the main panel, and i green the picturebox.
private void image_Scroll(object sender, EventArgs e)
{
int pbWidth = pbImg.Image.Width + (pbImg.Image.Width * ztb.Value / 100);
int pbHeight = pbImg.Image.Height + (pbImg.Image.Height * ztb.Value / 100);
int mpWidth = mainPanel.Width:
int mpHeight = mainPanel.Height;
int pbX = (mpWidth - pbWidth) / 2;
int pbY = (mpHeight - pbHeight) / 2;
pbImg.Size = new Size(pbWidth, pbHeight);
Point p = new Point(pbX, pbY);
pictureBox.Location = p;
}

Related

How to move drawn graphics by dragging its body in C#?

I'm currently making a program to drawn lines by coordinates and the lines can be edited or moved.
For now if want to move or edit the line, the cursor location will be the new A or B point. so moving the line is only possible by dragging the edge.
Is there any method to make it possible to drag it by the body?
For the lines I'm using
Graphics g = e.Graphics;
Pen pen1 = new Pen(Color.Black, 3);
g.DrawLine(pen1, line1X1, line1Y1, line1X2, line1Y2);
pen1.Dispose();
and for the edge calculation in mousemove event right now I`m using
int oldX1 = line1X1; //Saving old coordinates for calculation
int oldY1 = line1Y1;
int oldX2 = line1X2;
int oldY2 = line1Y2;
int newX1 = e.Location.X; //New coordinates for calculation
int newY1 = e.Location.Y;
int difX = newX1 - oldX1; //Calculate the difference between old & new coordinates
int difY = newY1 - oldY1;
line1X1 = e.Location.X; //Move point 1 coordinates to new one
line1Y1 = e.Location.Y;
line1X2 = oldX2 + difX; //Move point 2 coordinates to old point 2 + difference
line1Y2 = oldY2 + difY;
x1Box.Text = line1X1.ToString();
y1Box.Text = line1Y1.ToString();
x2Box.Text = line1X2.ToString();
y2Box.Text = line1Y2.ToString();
tried to make 2nd calculation in mousemove event for click & drag anywhere on the screen to move but its not moving at all. if changed the calculation a bit it moved but shaking weirdly to both sides of the cursor example top & bottom of the cursor with same distance or left & right and keep swapping places when moving the cursor.
int clickPointX = e.Location.X;
int clickPointY = e.Location.Y;
int oldX1 = line1X1;
int oldY1 = line1Y1;
int difX = clickPointX - oldX1;
int difY = clickPointY - oldY1;
int dif1to2X = line1X2 - oldX1;
int dif1to2Y = line1Y2 - oldY1;
line1X1 = clickPointX - difX;
line1Y1 = clickPointY - difY;
line1X2 = line1X1 + dif1to2X;
line1Y2 = line1Y1 + dif1to2Y;
x1Box.Text = line1X1.ToString();
y1Box.Text = line1Y1.ToString();
x2Box.Text = line1X2.ToString();
y2Box.Text = line1Y2.ToString();
application image
Question is.
Anyone know how to move the line by clicking & dragging the body not the edge?
Anyone know what is wrong with my 2nd calculation to move the line by click & drag anywhere?
Thank you very much

Visual Basic PowerPacks Oval Shape control, displayed as a Circle on the form and I want to increase its size from the center

I have a Visual Basic PowerPacks Oval Shape control, displayed as a Circle on the form and I want to increase its size from the center of the last position, so that I can increase it from all sides i.e. the center, how can I do it, I tried this.
//This code to draw circle on the clicked area.
MouseEventArgs clickArgs = (MouseEventArgs)e; Graphics graphics = this.CreateGraphics();
float circlesize = 100;
int x = Convert.ToInt32(clickArgs.X - circlesize / 2);
int y = Convert.ToInt32(clickArgs.Y - circlesize / 2);
ovalShape1.Location = new Point(x, y);
//To increase its size
ovalShape1.Width = ovalShape1.Width + 10;
ovalShape1.Height = ovalShape1.Height + 10;
The Current code is increasing the size from the last X,Y Position which is not correct. :(

How to zoom in using Mouse Position on that image

I have a userControl library, which consists of the main Panel and a PictureBox, I want to make a zoomable PictureBox tool, I zoom in and out using mouseWheel event of the main Panel, the problem that I can't figure out how do I zoom in by the mouse position on the image, so whenever I zoom in, the zoom goes the Top-Left corner of the panel, so how do I fix that?
private double ZOOMFACTOR = 1.15; // = 15% smaller or larger
private int MINMAX = 5;
void picPanel_MouseWheel(object sender, MouseEventArgs e)
{
if (e.Delta > 0)
{
ZoomIn();
}
else
{
ZoomOut();
}
}
private void ZoomIn()
{
if ((picBox.Width < (MINMAX * this.Width)) &&
(picBox.Height < (MINMAX * this.Height)))
{
picBox.Width = Convert.ToInt32(picBox.Width * ZOOMFACTOR);
picBox.Height = Convert.ToInt32(picBox.Height * ZOOMFACTOR);
}
}
private void picBox_MouseEnter(object sender, EventArgs e)
{
if (picBox.Focused) return;
picBox.Focus();
}
Update :
I have tried this, it looks like working, but not exactly as it should be!! Any ideas?
private void ZoomIn()
{
if ((picBox.Width < (MINMAX * this.Width)) &&
(picBox.Height < (MINMAX * this.Height)))
{
picBox.Width = Convert.ToInt32(picBox.Width * ZOOMFACTOR);
picBox.Height = Convert.ToInt32(picBox.Height * ZOOMFACTOR);
Point p = this.AutoScrollPosition;
int deltaX = e.X - p.X;
int deltaY = e.Y - p.Y;
this.AutoScrollPosition = new Point(deltaX, deltaY);
}
}
This is the example of Zoom image on mouse position....
tested verified.
protected override void OnMouseWheel(MouseEventArgs ea)
{
// flag = 1;
// Override OnMouseWheel event, for zooming in/out with the scroll wheel
if (picmap1.Image != null)
{
// If the mouse wheel is moved forward (Zoom in)
if (ea.Delta > 0)
{
// Check if the pictureBox dimensions are in range (15 is the minimum and maximum zoom level)
if ((picmap1.Width < (15 * this.Width)) && (picmap1.Height < (15 * this.Height)))
{
// Change the size of the picturebox, multiply it by the ZOOMFACTOR
picmap1.Width = (int)(picmap1.Width * 1.25);
picmap1.Height = (int)(picmap1.Height * 1.25);
// Formula to move the picturebox, to zoom in the point selected by the mouse cursor
picmap1.Top = (int)(ea.Y - 1.25 * (ea.Y - picmap1.Top));
picmap1.Left = (int)(ea.X - 1.25 * (ea.X - picmap1.Left));
}
}
else
{
// Check if the pictureBox dimensions are in range (15 is the minimum and maximum zoom level)
if ((picmap1.Width > (imagemappan.Width)) && (picmap1.Height > (imagemappan.Height)))
{
// Change the size of the picturebox, divide it by the ZOOMFACTOR
picmap1.Width = (int)(picmap1.Width / 1.25);
picmap1.Height = (int)(picmap1.Height / 1.25);
// Formula to move the picturebox, to zoom in the point selected by the mouse cursor
picmap1.Top = (int)(ea.Y - 0.80 * (ea.Y - picmap1.Top));
picmap1.Left = (int)(ea.X - 0.80 * (ea.X - picmap1.Left));
}
}
}
}
The problem is that your control is acting like a viewport - the origin is top left, so every time you stretch the image you're doing it from that corner - the upshot is you wind up zooming into the top left corner, you need to offset the stretched image and centre the point the user zoomed in on.
image size: 200,200
user clicks 100,50 and zooms in x2
stretch the image
image size 400,400, and the place the user clicked is now effectively at 200,100
you need to slide the image 100 px left and 50 px up to correct for re-sizing the image
You'll need to override the paint event handler to draw the image offset:
RectangleF BmpRect = new RectangleF((float)(Offset.X), (float)(Offset.Y), (float)(ZoomedWidth), (float)(ZoomedHeight));
e.Graphics.DrawImage(Bmp, ViewPort , BmpRect, GraphicsUnit.Pixel);
Bmp is your image; ViewPort is a Rectangle defined by your pictureBox control
Here is a thread that might help.

Keeping a PictureBox centered inside a container

I am designing a simple picture viewer with ability to do some basic image processing. At the moment I have the problem of keeping the PictureBox centered inside a TabPage all the time as well as keeping the picturebox width and size same as the picture its showing. So far I had no success.
I have the following code that I call in form constructor to position it in center. it works the first time to center the picturebox:
private void SetPictureBoxOriginalSizeAndLocation(bool makeImageNull = false, DockStyle dockStyle = DockStyle.None)
{
if (makeImageNull) picBoxView.Image = null;
picBoxView.Dock = dockStyle;
var xPoint = tabImageView.Location.X + ((splitContainer.Panel2.Width / 2) / 2);
var yPoint = tabImageView.Location.Y;
var width = tabImageView.Width / 2;
var height = (tabImageView.Height / 2) - toolStripImageView.Height;
if (picBoxView.Image == null) return;
//Resize image according to width
picBoxView.Image = ImageMethods.ResizeImage(picBoxView.Image.Tag.ToString(), width, height, false);
picBoxView.Location = new Point(xPoint, yPoint);
picBoxView.Width = width;
picBoxView.Height = height;
}
But it does not resize the picturebox to its image (you can see the black part that is back color for the picturebox control):
The problem is getting worse as soon as I resize the form, the picturebox position will goes to top:
I call the code above in form's resize event as well, no idea why it works when application starts. Would be nice if someone can tell me what properties I should take care of to achieve a nicely centered picturebox which always is as big as its image.
It's pretty easy if you just set the Anchor style to none:
picBoxView = new PictureBox();
picBoxView.SizeMode = PictureBoxSizeMode.AutoSize;
picBoxView.Anchor = AnchorStyles.None;
tabImageView.Controls.Add(picBoxView);
CenterPictureBox(picBoxView, myImage);
Then just center the PictureBox initially whenever you change the image of the PictureBox:
private void CenterPictureBox(PictureBox picBox, Bitmap picImage) {
picBox.Image = picImage;
picBox.Location = new Point((picBox.Parent.ClientSize.Width / 2) - (picImage.Width / 2),
(picBox.Parent.ClientSize.Height / 2) - (picImage.Height / 2));
picBox.Refresh();
}
Having the Anchor = None will center the PictureBox control for you whenever the parent container gets resized because it "isn't" anchored to the default Left and Top locations.
Givien a Form with TabControl, which has Dock set to Fill, below will keep your PictureBox in the centre. It also sets PictureBox size to Bitmap size:
public partial class Form1 : Form
{
Bitmap b = new Bitmap(320, 200);
public Form1()
{
InitializeComponent();
CenterTheBox();
}
private void Form1_Resize(object sender, EventArgs e)
{
CenterTheBox();
}
void CenterTheBox()
{
pictureBox1.Size = b.Size;
var left = (tabPage1.ClientRectangle.Width - pictureBox1.ClientRectangle.Width) / 2;
var top = (tabPage1.ClientRectangle.Height - pictureBox1.ClientRectangle.Height) / 2;
pictureBox1.Location = new Point(tabPage1.ClientRectangle.Location.X + left, tabPage1.ClientRectangle.Location.Y + top);
}
}
I believe your problem lies here
var xPoint = tabImageView.Location.X + ((splitContainer.Panel2.Width / 2) / 2);
var yPoint = tabImageView.Location.Y;
var width = tabImageView.Width / 2;
var height = (tabImageView.Height / 2) - toolStripImageView.Height;
ypoint is alwways set to tabImageView Y, althought it should be set to
tabImageView.Location.Y + (tabImageView.Size.Height - picBoxView.Size.Height)/2
should be almost the same with xPoint
tabImageView.Location.X + (tabImageView.Size.Width - picBoxView.Size.Width)/2
and
width = picBoxView.Image.Width;
height = picBoxView.Image.Height;

easier way to create grid than creating 900 controls c#.net

I'm trying to write a program that creates a 30x30 grid of 20px x 20px boxes. When you click on a box in the grid it changes the color of the box and stores the RGB value of it and the x,y coordinates (hidden in the box). So basically I'm making a simple paint program. The purpose of it is to assist me in programming LED RGB animations for a 30x30 pixel grid. So once I draw something I export the X,Y,R,G,B of each pixel in the image.
So my question is, is there an easy way to do this. With out creating 900 buttons and putting them together? I've got something sorta working :
Panel BU = new Panel();
BU.AutoSize = false;
BU.Location = new System.Drawing.Point(xpos, ypos);
BU.BackColor = System.Drawing.Color.Transparent;
BU.Font = new System.Drawing.Font("Microsoft Sans Serif", 5, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
BU.Name = row_num + " x " + col_num;
BU.Size = new System.Drawing.Size(20, 20);
BU.MouseDown +=new MouseEventHandler(BU_MouseDown);
BU.MouseEnter +=new EventHandler(BU_MouseEnter);
this.Controls.Add(BU);
xpos = xpos + 20;
px_num++;
col_num++;
if (col_num == 30)
{
col_num = 0;
ypos = ypos + 20;
row_num++;
xpos = 0;
};
But it takes WAAAAAY to long to load.
Yes.
Create one panel, and handle the grid painting and mouse events inside that.
Super simple example, optimized for nothing and flicker happy:
private Color[,] _Colors = new Color[30, 30];
private void panel1_Paint(object sender, PaintEventArgs e) {
int left = 0;
int top = 0;
for (int y = 0; y < 30; y++) {
left = 0;
for (int x = 0; x < 30; x++) {
Rectangle r = new Rectangle(left, top, 20, 20);
using (SolidBrush sb = new SolidBrush(_Colors[x, y]))
e.Graphics.FillRectangle(sb, r);
ControlPaint.DrawBorder3D(e.Graphics, r, Border3DStyle.Raised, Border3DSide.Left | Border3DSide.Top | Border3DSide.Right | Border3DSide.Bottom);
left += 20;
}
top += 20;
}
}
private void panel1_MouseDown(object sender, MouseEventArgs e) {
if (e.Button == MouseButtons.Left) {
int left = 0;
int top = 0;
for (int y = 0; y < 30; y++) {
left = 0;
for (int x = 0; x < 30; x++) {
Rectangle r = new Rectangle(left, top, 20, 20);
if (r.Contains(e.Location)) {
_Colors[x, y] = Color.Red;
panel1.Invalidate();
}
left += 20;
}
top += 20;
}
}
}
A custom user control that renders 20x20 boxes would do the trick. To get the location of a mouse click, take the x and y values, divide by 20.
Something very similar here: https://github.com/i-e-b/DBSS/blob/master/DBSS/BigGrid/SheetView.cs
Well, instead of creating 900 controls, no difference if it's made in WindowsForms, or WPF with HD accelaration, it will be slow.
What can do, if you really need always have all controls visible on the screen, so potentially at some points also have 900 contemporary, is just draw rectangles. You can do an
emulation of the button.
Draw Rectangle, where Left and Top lines are darker then Right and Bottom, will give 3D filling to user, make it inverse colors of lines, and it will give to user a filling of pushed button. For sure you need to handle all mouse interactions, like MouseMove, MouseDown, MouseUp on your panel (cause the panel will be the only control present at this point), and figure out on what rectangle event happens and draw that rectangle accordingly.
I typically use a Bitmap object for this and use one of the overrides in Graphics.DrawImage to draw it zoomed in. Then I draw grid lines over top of the zoomed image.
I'm digging up some source code for you now.
Edit
Sorry, no source code handy at the moment. But basically what you want to do is create a new system.drawing.bitmap that is 30x30 pixels. In the paint even of teh control you want to draw it in, use the passed in Graphics Object (usually e.graphics) and call .DrawImage. Use one of the overloads that allows you to specify the output size so that you can scale it out by whatever zoom factor you want. You will also need to set the .PixelOffsetMode on the graphics object to .none, or else things will be offset by 1/2 your zoom. Set .InterpolationMode to .NearestNeighbor so that the output isn't blurry. This should give you a perfectly aligned "pixelated" zoomed image. Then just loop and draw horizontal and vertical grid lines.
To handle the mouse clicks just add a handler to the controls mouse down event, and divide the input position by your zoom factor to get the real x and y coords. then update that pixel of the source image and call .invalidate on the control you are drawing to. That will cause it to repaint with the updated image.

Categories