Panel and ScrollBar in Windows forms - c#

I am using a panel to display a image in windows forms
I am drawing image in the panel in Panel_Paint event as follows:
Graphics g = panel1.CreateGraphics();
Image im = new Bitmap(#"../../Data/#3_Page2.PNG");
g.DrawImage(im,new Point(10,10));
Now, the image is drawn as i expected, with some part of the bottom of the image not displaying as its height is greater than forms height.
I have added the VScrollBar now. How do i make that panel to view the rest of the image with the help of VScrollBar.

You can use PictureBox with SizeMode set to AutoSize and AutoScroll property of Panel. That way the panel should add scrollbars if they are needed.
PictureBox pictureBox = new System.Windows.Forms.PictureBox();
pictureBox.Image = new Bitmap(#"../../Data/#3_Page2.PNG");
pictureBox.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize;
panel.AutoScroll = true;
panel.Controls.Add(this.pictureBox);

This solution works, however if your image is large enough, there is a little flicker when you scroll (however it's acceptable). First you have to add a VScrollBar right on the right of your panel and a HScrollBar right under the bottom of your panel. This demo requires you have a VScrollBar named vScrollBar1 and HScrollBar named hScrollBar1, a Button named buttonOpenImage to allow user to open some image, a Panel named panel1 used as the main area to draw the image:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
//To prevent/eliminate flicker, do this
typeof(Panel).GetProperty("DoubleBuffered",
System.Reflection.BindingFlags.Instance |
System.Reflection.BindingFlags.NonPublic).SetValue(panel1, true, null);
//------------------------
UpdateScrollbarsState();
}
int imgWidth, imgHeight;
Image image;
Point leftTop = Point.Empty;
int lastV, lastH;
private void OpenImage(){
OpenFileDialog openFile = new OpenFileDialog();
openFile.FileOk += (s, e) => {
try {
image = Image.FromFile(openFile.FileName);
//calculate the physical size of the image based on the resolution and its logical size.
//We have to do this because the DrawImage is based on the physical size (not logical).
imgWidth = (int)(image.Width * 96f / image.HorizontalResolution + 0.5);
imgHeight = (int)(image.Height * 96f / image.VerticalResolution + 0.5);
lastV = lastH = 0;
UpdateScrollbarsState();
vScrollBar1.Value = 0;
hScrollBar1.Value = 0;
panel1.Invalidate();
}
catch {
image = null;
MessageBox.Show("Image file is invalid or corrupted!");
}
};
openFile.ShowDialog();
}
private void UpdateScrollbarsState() {
//We have to update all the info about Minimum and Maximum
vScrollBar1.Minimum = 0;
hScrollBar1.Minimum = 0;
vScrollBar1.Maximum = Math.Max(imgHeight-panel1.Height,0);
hScrollBar1.Maximum = Math.Max(imgWidth-panel1.Width,0);
vScrollBar1.Visible = vScrollBar1.Maximum > 0;
hScrollBar1.Visible = hScrollBar1.Maximum > 0;
if (vScrollBar1.Maximum == 0) {
leftTop.Y = 0;
lastV = 0;
}
if (hScrollBar1.Maximum == 0) {
leftTop.X = 0;
lastH = 0;
}
}
private void panel1_Paint(object sender, PaintEventArgs e) {
if (image == null) return;
e.Graphics.DrawImage(image, leftTop);
}
//The ValueChanged event handler of your vScrollBar1
private void vScrollBar1_ValueChanged(object sender, EventArgs e) {
if (!vScrollBar1.Visible) return;
leftTop.Offset(0, -vScrollBar1.Value + lastV);
lastV = vScrollBar1.Value;
panel1.Invalidate();
}
//The ValueChanged event handler of your hScrollBar1
private void hScrollBar1_ValueChanged(object sender, EventArgs e) {
if (!hScrollBar1.Visible) return;
leftTop.Offset(lastH - hScrollBar1.Value, 0);
lastH = hScrollBar1.Value;
panel1.Invalidate();
}
//handler for SizeChanged event of the panel. However if resizing
//the form causes the panel's size changing, you should attach this
//handler for form.
private void panel1_SizeChanged(object sender, EventArgs e) {
UpdateScrollbarsState();
}
//handler for the Click event of a button (click to open an image)
private void buttonOpenImage_Click(object sender, EventArgs e) {
OpenImage();
}
}

Related

how can I select the pictures loaded in windows Form in c#?

I have some pictures in a database which I retrieve them . To load these pictures, I made a "Tab Control" in Windows Form, which has a "Tab page1". when the program runs, a group box, containing a PictureBox (and some other text boxes), will be created for each picture. my pictures can be load in these picture boxes, and I will have a list of group boxes(gbList). However, I can not select these pictures during the run. Can anybody suggest a solution?
private void Form2_Load(object sender, EventArgs e)
{
tabPage1.Controls.Clear();
int x = 0, y = 0;
int j = 0;
for (int i = 0; i < output.Count - 1; i++)
{
PictureBox pic = new PictureBox();
pic.SizeMode = PictureBoxSizeMode.StretchImage;
SelectablegroupBox gb = new SelectablegroupBox();
gb.Controls.Add(pic);
gbList.Add(gb);
//to retrieve the images from the database in ProductImages class: (output is the result of a query of database)
ProductImages pI = output[i];
imgbyte = pI.Pic;
using (MemoryStream ms = new MemoryStream(imgbyte))
{
Image img = Image.FromStream(ms);
pic.Image = img;
}
//to add the group box list o the tabpage:
tabPage1.Controls.Add(gbList[j]);
gbList[j].Location = new Point(x, y);
y += gbList[i].Height;
j++;
}
here is my problem. I want the user to be able to select the images (Then I want to save these selected Items). But the "result" is always empty:
var result = from s in gbList
where s.Focused ==true
select s;
foreach (var s in result)
{ //save the selected images}
As I learned from another post, I defined SelectablegroupBox" as:
class SelectablegroupBox : GroupBox
{
public SelectablegroupBox()
{
this.SetStyle(ControlStyles.Selectable, true);
this.TabStop = true;
}
protected override void OnEnter(EventArgs e)
{
this.Focus();
this.Invalidate();
base.OnEnter(e);
}
protected override void OnPaint(PaintEventArgs pe)
{
base.OnPaint(pe);
if (this.Focused)
{
var rc = this.ClientRectangle;
rc.Inflate(-2, -2);
ControlPaint.DrawFocusRectangle(pe.Graphics, rc);
}
}
}
thanks in advance
Your class SelectableGroupBox is not suitable to let the user select one or more images. There can be at most one focused control in your app - this will be probably a button on your form which the user clicks to save the selected images.
One simple solution would be to use CheckBox controls with the Appearance property set to Button. Also, you don't have to layout the images manually, let a FlowLayoutPanel do the job.
First, add a FlowLayoutPanel named flowLayoutPanel to your tabPage2 and set the following Properties:
flowLayoutPanel.AutoScroll = true;
flowLayoutPanel.Dock = System.Windows.Forms.DockStyle.Fill;
Then change the appropriate code in Form2 to:
private const int imageWidth = 128;
private const int imageHeight = 128;
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
for (int i = 0; i < output.Count; i++)
{
CheckBox cb = new CheckBox();
cb.Appearance = Appearance.Button;
cb.Size = new Size(imageWidth, imageHeight);
cb.BackgroundImageLayout = ImageLayout.Zoom;
ProductImages pI = output[i];
//Don't dispose the MemoryStream, the Image class will need it!
var ms = new MemoryStream(pI.Pic);
cb.BackgroundImage = Image.FromStream(ms);
flowLayoutPanel.Controls.Add(cb);
}
}
This is how you get your selected pictures:
private void SaveButton_Click(object sender, EventArgs e)
{
var selected = flowLayoutPanel.Controls.OfType<CheckBox>().Where(x => x.Checked);
Debug.Print("Selected images: {0}", selected.Count());
foreach (var item in selected)
{
//Save the picture from item.BackgroundImage.
}
}

C# ImageBox Clear Rectangle on MouseUp

I have a panel with multiple picturebox created at runtime.
The user will create a rectangle on any picture box and the selected part will be displayed on a preview picturebox.
I have successfully done the above using the below code.
Question
I want to clear the selection rectangle at mouseup event. Used invalidate but not working.
From how to clear the graphics(rectangle shape) in picturebox
Also, when I scroll the panel the same rectangle(mouse selection) is shown on all picturebox.
private void Picture_Paint(object sender, PaintEventArgs e)
{
if (Rect!=null && Rect.Width>0 && Rect.Height>0)
{
e.Graphics.FillRectangle(selectionBrush, Rect);
}
}
private void Picture_MouseDown(object sender, MouseEventArgs e)
{
RecStartpoint = e.Location;
((PictureBox)sender).Invalidate();
}
private void Picture_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button != MouseButtons.Left) return;
Point tempEndPoint = e.Location;
Rect.Location = new Point(
Math.Min(RecStartpoint.X, tempEndPoint.X),
Math.Min(RecStartpoint.Y, tempEndPoint.Y));
Rect.Size = new Size(
Math.Abs(RecStartpoint.X - tempEndPoint.X),
Math.Abs(RecStartpoint.Y - tempEndPoint.Y));
((PictureBox)sender).Invalidate();
}
private void Picture_MouseUp(object sender, MouseEventArgs e)
{
if (e.Button != MouseButtons.Left) return;
PictureBox org_pic = (PictureBox)(sender);
Point RecEndpoint=e.Location;
int xDown = Math.Min(RecStartpoint.X,RecEndpoint.X);
int yDown = Math.Min(RecStartpoint.Y, RecEndpoint.Y);
int xUp = Math.Max(RecStartpoint.X,RecEndpoint.X);
int yUp = Math.Max(RecStartpoint.Y,RecEndpoint.Y);
Rectangle rec = new Rectangle(xDown, yDown, Math.Abs(xUp - xDown), Math.Abs(yUp - yDown));
xDown = xDown * org_pic.Image.Width / org_pic.Width;
yDown = yDown * org_pic.Image.Height / org_pic.Height;
xUp = xUp * org_pic.Image.Width / org_pic.Width;
yUp = yUp * org_pic.Image.Height / org_pic.Height;
rectCropArea = new Rectangle(xDown, yDown, Math.Abs(xUp - xDown), Math.Abs(yUp - yDown));
pictureBox_preview_photo.Refresh();
Bitmap sourceBitmap = new Bitmap(org_pic.ImageLocation);
Graphics g = pictureBox_preview_photo.CreateGraphics();
g.DrawImage(sourceBitmap, new Rectangle(0, 0, pictureBox_preview_photo.Width, pictureBox_preview_photo.Height), rectCropArea, GraphicsUnit.Pixel);
}
I would try this approach:
First, make Image variable, in the scope of form
public partial class Form1 : Form
{
//variable for holding original image, before rectangle is drawn on it
Image originalImage = null;
public Form1()
{
InitializeComponent();
}
//rest of form's code...
second, save current picture in that variable on MouseDown
private void Picture_MouseDown(object sender, MouseEventArgs e)
{
//save it
startImage = ((PictureBox)sender).Image;
RecStartpoint = e.Location;
((PictureBox)sender).Invalidate();
}
lastly, on the end of MouseUp event, set Rectangle's width and height to zero and restore saved, original image
//snipped code
pictureBox_preview_photo.Refresh();
Bitmap sourceBitmap = new Bitmap(org_pic.ImageLocation);
Graphics g = pictureBox_preview_photo.CreateGraphics();
g.DrawImage(sourceBitmap, new Rectangle(0, 0, pictureBox_preview_photo.Width, pictureBox_preview_photo.Height), rectCropArea, GraphicsUnit.Pixel);
//make rectangle's widht and height 0 so that Paint event won't draw it
Rect.Width = Rect.Height = 0;
//restore image
this.Picture.Image = startImage;
I didn't understand that second question.

Using control functions in a bitmap in C#

I was thinking of implementing a bitmap into a picturebox (or the otherway round), such that I could have the option to:
1.drag and drop the item (Control function with mousebuttons)
2.rotate the item (bitmap function).
This is what I have currently:
Bitmap bmp = new Bitmap(#"my source");
PictureBox item = new System.Windows.Forms.PictureBox();
I create the picturebox on the click of a button:
private void button2_Click(object sender, EventArgs e)
{
item.BackColor = Color.Blue;
item.Location = new Point(400, 200);
item.MouseDown += new MouseEventHandler(textbox_MouseDown);
item.MouseMove += new MouseEventHandler(textbox_MouseMove);
item.MouseUp += new MouseEventHandler(textbox_MouseUp);
item.MouseWheel += new MouseEventHandler(textbox_MouseWheel);
item.Image = bmp;
item.SizeMode = PictureBoxSizeMode.StretchImage;
this.Controls.Add(item);
item.BringToFront();
}
and in the textbox_MouseMove void, I try to use the middle button to signal the rotation of the image
void textbox_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Middle )
{
if (activeControl == null || activeControl != sender)
{
return;
}
this.Paint += new PaintEventHandler(objectrotation_Paint);
Invalidate();
}
}
where the PaintEvent is responsible for the rotation of the image
private void objectrotation_Paint(object sender, PaintEventArgs d)
{
int dpi = 96;
bmp.SetResolution(dpi, dpi);
if (bmp != null)
{
float bw2 = bmp.Width / 2f;
float bh2 = bmp.Height / 2f;
d.Graphics.TranslateTransform(bw2, bh2);
d.Graphics.RotateTransform(angle);
d.Graphics.TranslateTransform(-bw2, -bh2);
d.Graphics.DrawImage(bmp,0,0);
d.Graphics.ResetTransform();
}
}
This however would create two sets of images, i.e. when I press button 2 I get one image, when I move my mouse with middle button down I get the second image (rotating).Refering to some of the questions on here, people recommend using
item.Image = bmp;
but it simply copies the from picturebox to the bitmap while they have seperate controls. While the picturebox class could not perform rotations without implementing a Bitmap, the Bitmap class does not have the option to perform actions like OnMouseMove etc.
Is there any way to combine my bitmap and the picturebox such that I could achieve my two goals?

I'm trying to show the current image in the pictureBox and stretch it but it's not showing anything why?

In For1 i have this code:
private void timer1_Tick(object sender, EventArgs e)
{
try
{
this.pictureBox1.SizeMode = PictureBoxSizeMode.StretchImage;
pictureBox1.Load(file_array_satellite[file_indxs_satellite]);
file_indxs_satellite = file_indxs_satellite - 1;
if (file_indxs_satellite < 0)
{
file_indxs_satellite = file_array_satellite.Length - 1;
}
}
catch
{
timer1.Enabled = false;
}
}
private void satellitesToolStripMenuItem_Click(object sender, EventArgs e)
{
file_array_satellite = Directory.GetFiles(UrlsPath, "RainImage*.*");
if (file_array_satellite.Length > 0)
{
DateTime[] creationTimes8 = new DateTime[file_array_satellite.Length];
for (int i = 0; i < file_array_satellite.Length; i++)
creationTimes8[i] = new FileInfo(file_array_satellite[i]).CreationTime;
Array.Sort(creationTimes8, file_array_satellite);
file_indxs_satellite = 0;
file_indxs_satellite = file_array_satellite.Length - 1;
timer1.Enabled = true;
}
}
private void pictureBox1_MouseEnter(object sender, EventArgs e)
{
this.pictureBox1.Size = new Size(500, 500);
pictureBox1.Location = new Point(this.Bounds.Width / 2,
this.Bounds.Height / 2);
this.pictureBox1.SizeMode = PictureBoxSizeMode.StretchImage;
pictureBox1.BringToFront();
}
private void pictureBox1_MouseLeave(object sender, EventArgs e)
{
this.pictureBox1.Size = new Size(100, 100);
pictureBox1.Location = new Point(12,
27);
}
In the original the picturebox1 size is 100x100 and each image i stretch to fit in the pictureBox.
When it's 100x100 everything is ok i see the animation of each image in the pictureBox.
Now i did an event that when i enter with the mouse to the pictureBox area it should move to the center of the form resize to 500x500 stretch the images and show the same animation.
And when i leave the pictureBox area it should return to it's original size and location.
When i enter with the mouse to the pictureBox1 area the pictureBox just vanish i don't see it anywhere once i leave the pictureBox area i see it 100x100 in it's original place and size.
Why when i enter with the mouse to the pictureBox1 area it's vanish i don't see it in the center of the form on size 500x500 ?
file_array_satellite is string[] and file_indxs_satellite is int.
RainImage*.* are the files names on the hard disk after downloaded them.
The idea is not to convert/change the files sizes on the hard disk each time i enter or leave so i wanted that once i enter the pictureBox1 area it will stretch the current image in the pictureBox and show it . It's working when it's 100x100 but not on 500x500.
When you mouse over the PictureBox and move it to the center of the form, you are moving it out from under the mouse cursor. This causes the MouseLeave event to immediately trigger, which places it back under your mouse cursor again, which causes the MouseEnter event to trigger again, etc.
You can do something like this:
bool suppressMouseLeave;
private void pictureBox1_MouseEnter(object sender, EventArgs e)
{
suppressMouseLeave = true;
this.pictureBox1.Size = new Size(500, 500);
pictureBox1.Location = new Point(this.Bounds.Width / 2,
this.Bounds.Height / 2);
this.pictureBox1.SizeMode = PictureBoxSizeMode.StretchImage;
pictureBox1.BringToFront();
//point the cursor to the new Position so that it's still kept on the pictureBox1
//This is important because it makes your idea acceptable.
//Otherwise you have to move your mouse onto your pictureBox and leave the
//mouse from it then to restore the pictureBox
Cursor.Position = PointToScreen(new Point(pictureBox1.Left + 250, pictureBox1.Top + 250));
suppressMouseLeave = false;
}
private void pictureBox1_MouseLeave(object sender, EventArgs e)
{
if(suppressMouseLeave) return;
this.pictureBox1.Size = new Size(100, 100);
pictureBox1.Location = new Point(12, 27);
}
I would venture a guess that this.Bounds.Width and this.Bounds.Height are not what you expect them to be, so the PictureBox isn't vanishing, you are just setting it to some location that is offscreen/off your form. Run Visual Studio in Debug mode and put a breakpoint around that line and see what this.Bounds is equal to. This may give you a clue as to the proper location you need to set.
How about in "in place" zoom like this?
private void pictureBox1_MouseEnter(object sender, EventArgs e)
{
Rectangle rc = pictureBox1.Bounds;
rc.Inflate(200, 200);
pictureBox1.Bounds = rc;
pictureBox1.BringToFront();
}
private void pictureBox1_MouseLeave(object sender, EventArgs e)
{
Rectangle rc = pictureBox1.Bounds;
rc.Inflate(-200, -200);
pictureBox1.Bounds = rc;
}

How to scroll in flowlayout panel without showing scrollbar in windows form

I am working on a touch screen POS in WinForms.
I have a flowlayoutpanel and add buttons dynamically but I dont want to show a scrollbar.
I use 2 buttons to scroll instead, so please help me how to scroll without showing a scrollbar
Try placing the FlowLayoutPanel inside another panel with these properties:
flowLayoutPanel1.AutoScroll = false;
flowLayoutPanel1.AutoSize = true;
flowLayoutPanel1.AutoSizeMode = AutoSizeMode.GrowAndShrink;
From here, you have to control yourself the location of the FlowLayoutPanel1 inside your panel (which should also have AutoScroll = false;) based on your two buttons.
Take two buttons btnLeft and btnRight and try this code :
private void btnLeft_Click(object sender, EventArgs e)
{
if (flowPanelItemCategory.Location.X <= xpos)
{
xmin = flowPanelItemCategory.HorizontalScroll.Minimum;
if (flowPanelItemCategory.Location.X >= xmin)
{
xpos -= 100;
flowPanelItemCategory.Location = new Point(xpos, 0);
}
}
}
private void btnRight_Click(object sender, EventArgs e)
{
if (flowPanelItemCategory.Location.X <= xpos)
{
xmax = flowPanelItemCategory.HorizontalScroll.Maximum;
if (flowPanelItemCategory.Location.X < xmax)
{
xpos += 100;
flowPanelItemCategory.Location = new Point(xpos, 0);
}
}
}

Categories