Obtain full-page screenshot from WebBrower component - c#

I am attempting to capture a full-page screenshot of any website a user is viewing using the WebBrowser component.
At present, I am able to only able to capture what a user is viewing from within the WebBrowser. However, the screenshot image created is the size of the webpage. For example, below is a (half-sized) screenshot of the BBC website, the black area is actually saved transparent but I've filled it black for visibility.
I have seen solutions where a new WebBrowser instance is used to fetch a fullpage snapshot. However, I need the screenshot to be exactly of the page as the user is viewing it at the time, much like how the full-page screenshot works in Firefox.
My code below that generated the above image:
private void button1_Click(object sender, EventArgs e)
{
while (webBrowser1.ReadyState != WebBrowserReadyState.Complete)
{
Application.DoEvents();
}
int scrollWidth = 0;
int scrollHeight = 0;
scrollHeight = webBrowser1.Document.Body.ScrollRectangle.Height;
scrollWidth = webBrowser1.Document.Body.ScrollRectangle.Width;
webBrowser1.Size = new Size(scrollWidth, scrollHeight);
Bitmap bm = new Bitmap(scrollWidth, scrollHeight);
webBrowser1.DrawToBitmap(bm, new Rectangle(0, 0, bm.Width, bm.Height));
bm.Save(#"D:\Screenshots\test.png", ImageFormat.Png);
}

I've got a good working one..
private void button1_Click(object sender, EventArgs e)
{
using (FileDialog fd = new SaveFileDialog())
{
fd.Filter = "Image (*.png)|*.png";
if (fd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
new WebPageSnap(webBrowser1.Url.ToString(), fd.FileName);
//might take 3 or 4 seconds to save cauz it has to load again.
}
}
}
class WebPageSnap
{
WebBrowser wb;
string outFile;
public WebPageSnap(string url, string outputFile)
{
wb = new WebBrowser();
wb.ProgressChanged += wb_ProgressChanged;
outFile = outputFile;
wb.ScriptErrorsSuppressed = true;
wb.ScrollBarsEnabled = false;
wb.Navigate(url);
}
void wb_ProgressChanged(object sender, WebBrowserProgressChangedEventArgs e)
{
if (e.CurrentProgress == e.MaximumProgress)
{
wb.ProgressChanged -= wb_ProgressChanged;
try
{
int scrollWidth = 0;
int scrollHeight = 0;
scrollHeight = wb.Document.Body.ScrollRectangle.Height;
scrollWidth = wb.Document.Body.ScrollRectangle.Width;
wb.Size = new Size(scrollWidth, scrollHeight);
Bitmap bitmap = new Bitmap(wb.Width, wb.Height);
for (int Xcount = 0; Xcount < bitmap.Width; Xcount++)
for (int Ycount = 0; Ycount < bitmap.Height; Ycount++)
bitmap.SetPixel(Xcount, Ycount, Color.Black);
wb.DrawToBitmap(bitmap, new Rectangle(0, 0, wb.Width, wb.Height));
bitmap.Save(outFile, ImageFormat.Png);
}
catch { }
}
}
}
.
;Here's the result
.

Related

Why getting exception The process cannot access the file image0.gif' because it is being used by another process even if I dispose it first?

private void GenerateAnimatedGifs()
{
UnFreezWrapper unfreezWrapper = new UnFreezWrapper();
checkBoxGetImages = false;
checkBoxGetAllImages.Checked = false;
GetImagesFiles();
for (int i = 0; i < filesSatellite.Length; i++)
{
Image img = Image.FromFile(filesSatellite[i]);
img.Save(filesSatellite[i] + "ConvertedToGif.gif", System.Drawing.Imaging.ImageFormat.Gif);
img.Dispose();
File.Delete(filesSatellite[i]);
}
GetImagesFiles();
unfreezWrapper.MakeGIF(filesRadar.ToList(), #"d:\Downloaded Images\Animates Gifs\radanim.gif", 100, true);
unfreezWrapper.MakeGIF(filesSatellite.ToList(), #"d:\Downloaded Images\Animates Gifs\satanim.gif", 100, true);
}
In the loop I convert each image to gif save it in other name and then dispose the original image and then trying to delete the original image so only the ConvertedToGif images will left.
but I'm getting the exception is being used by another process on the delete line
File.Delete(filesSatellite[i]);
but isn't the file disposed already ?
The problem is that in the constructor I'm loading the images to a pictureBox using timer that is why the images are busy with another process.
If I'm not loading the images at the constructor everything will work fine.
But I want to display the images when running the application and also to be able to convert them to other formats and making other manipulations like creating animated gif of them.
I'm stuck here.
This is the constructor code
public Form1()
{
InitializeComponent();
CheckIfImagesExist();
}
And the code of the CheckIfImagesExist method
private void CheckIfImagesExist()
{
GetImagesFiles();
if (filesRadar != null)
{
if (filesRadar.Length > 1)
{
pictureBox1.Image = new Bitmap(filesRadar[0]);
trackBar1.Enabled = true;
timer1.Enabled = true;
}
if (filesRadar.Length == 1)
{
trackBar1.Enabled = false;
pictureBox1.Image = new Bitmap(filesRadar[0]);
}
}
if (filesSatellite != null)
{
if (filesSatellite.Length > 1)
{
pictureBox2.Image = new Bitmap(filesSatellite[0]);
trackBar1.Enabled = true;
timer2.Enabled = true;
}
if (filesSatellite.Length == 1)
{
trackBar1.Enabled = false;
pictureBox2.Image = new Bitmap(filesSatellite[0]);
}
}
}
The timer tick event
int satImagesCount = 0;
private void timer2_Tick(object sender, EventArgs e)
{
satImagesCount++;
if (satImagesCount == filesSatellite.Length)
{
satImagesCount = 0;
}
pictureBox2.Image = new Bitmap(filesSatellite[satImagesCount]);
if (isInsideSat)
{
pb.Image = new Bitmap(filesSatellite[satImagesCount]);
timer2.Interval = trackBar1.Value * 100;
}
}

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# WPF Thread accessed from a thread other than the

I'm trying to create a WPF application that takes a video and screenshots it every second while playing.
Code
public Form2(string url)
{
InitializeComponent();
axWindowsMediaPlayer1.URL = url;
new Thread(delegate(){
CheckFrame();
}).Start();
}
private void CheckFrame()
{
for (int i = 0; i < 100; i++)
{
Bitmap screenshot = new Bitmap(axWindowsMediaPlayer1.Bounds.Width, axWindowsMediaPlayer1.Bounds.Height);
Graphics g = Graphics.FromImage(screenshot);
g.CopyFromScreen(axWindowsMediaPlayer1.PointToScreen(
new System.Drawing.Point()).X,
axWindowsMediaPlayer1.PointToScreen(
new System.Drawing.Point()).Y, 0, 0, axWindowsMediaPlayer1.Bounds.Size);
pictureBox1.BackgroundImage = screenshot;
System.Threading.Thread.Sleep(1000);
}
}
When using the x y values of the media player itself I get the error
Additional information: Cross-thread operation not valid: Control 'axWindowsMediaPlayer1'
accessed from a thread other than the thread it was created on.
When using 0 as X/Y values so just 0px and 0px from the form point of view,it runs fine
In this case you can use Timer:
Timer tm = new Timer();
public Form2(string url)
{
InitializeComponent();
axWindowsMediaPlayer1.URL = url;
tm.Interval = 1000;
tm.Tick += tm_Tick;
tm.Start();
}
int i = -1;
void tm_Tick(object sender, EventArgs e)
{
if (++i < 100)
{
Bitmap screenshot = new Bitmap(axWindowsMediaPlayer1.Bounds.Width, axWindowsMediaPlayer1.Bounds.Height);
Graphics g = Graphics.FromImage(screenshot);
g.CopyFromScreen(axWindowsMediaPlayer1.PointToScreen(
new System.Drawing.Point()).X,
axWindowsMediaPlayer1.PointToScreen(
new System.Drawing.Point()).Y, 0, 0, axWindowsMediaPlayer1.Bounds.Size);
pictureBox1.BackgroundImage = screenshot;
}
else
{
i = -1;
tm.Stop();
}
}
Note: To take screenshot with your method, axWindowsMediaPlayer1 must visible on the screen.

Imagebox, converting bytearray to image so what is wrong?

I wanted to make an application which can read data from the webcam. It makes the really bright pixels red (right now). But I can't write it out into an imagebox. So what is the problem?
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
WebCam camera = new WebCam();
if (!camera.IsConnected())
{
camera.Connect();
}
else
{
Application.Exit();
}
// for (int x = 0; x <= 10000; x++)
// {
camera.Update();
MemoryStream ms = new MemoryStream();
camera.CalcBitmap().Save(ms, ImageFormat.Bmp);
byte[] bitmapData = ms.ToArray();
/*
int i = 54;
while (i <= (bitmapData.Length - 2))
{
if ((bitmapData[i] >= 240) & (bitmapData[i + 1] >= 240) & (bitmapData[i + 2] >= 240))
{
bitmapData[i] = 255;
bitmapData[i + 1] = 0;
bitmapData[i + 2] = 0;
i += 3;
}
}*/
MemoryStream stream = new MemoryStream(bitmapData);
pictureBox1.Image = new Bitmap(stream);
// }
}
So after lot of suffering I leaved the picturebox function empty and i wrote everything into a buttonclick function. And it worked! I still don't know what was the problem, but I think the picturebox funtoin tired to connect to the webcam even if it was connected.
public partial class Form1 : Form
{
public static WebCam camera = new WebCam();
private void button1_Click(object sender, EventArgs e)
{
if (!camera.IsConnected())
{
camera.Connect();
camera.Update();
MemoryStream ms = new MemoryStream();
camera.CalcBitmap().Save(ms, ImageFormat.Bmp);
byte[] bitmapData = ms.ToArray();
MemoryStream stream = new MemoryStream(bitmapData);
pictureBox1.Image = new Bitmap(stream);
}
else
{
Application.Exit();
}
}
public void pictureBox1_Paint(object sender, PaintEventArgs e)
{
}
You must call Dispose() on objects that holds unmanaged resources so the memory can be freed.
MemoryStream ms = null
try{
//your code
}
finally{
if(ms != null){
ms.Dispose()
}
}

c# Bitmap.Save A generic error occurred in GDI+ windows application

I am doing OCR application. I have this error when I run the system which the system will save the picturebox3.image into a folder.
//When user is selecting, RegionSelect = true
private bool RegionSelect = false;
private int x0, x1, y0, y1;
private Bitmap bmpImage;
private void loadImageBT_Click(object sender, EventArgs e)
{
try
{
OpenFileDialog open = new OpenFileDialog();
open.InitialDirectory = #"C:\Users\Shen\Desktop";
open.Filter = "Image Files(*.jpg; *.jpeg)|*.jpg; *.jpeg";
if (open.ShowDialog() == DialogResult.OK)
{
singleFileInfo = new FileInfo(open.FileName);
string dirName = System.IO.Path.GetDirectoryName(open.FileName);
loadTB.Text = open.FileName;
pictureBox1.Image = new Bitmap(open.FileName);
bmpImage = new Bitmap(pictureBox1.Image);
}
}
catch (Exception)
{
throw new ApplicationException("Failed loading image");
}
}
//User image selection Start Point
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
RegionSelect = true;
//Save the start point.
x0 = e.X;
y0 = e.Y;
}
//User select image progress
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
//Do nothing it we're not selecting an area.
if (!RegionSelect) return;
//Save the new point.
x1 = e.X;
y1 = e.Y;
//Make a Bitmap to display the selection rectangle.
Bitmap bm = new Bitmap(bmpImage);
//Draw the rectangle in the image.
using (Graphics g = Graphics.FromImage(bm))
{
g.DrawRectangle(Pens.Red, Math.Min(x0, x1), Math.Min(y0, y1), Math.Abs(x1 - x0), Math.Abs(y1 - y0));
}
//Temporary display the image.
pictureBox1.Image = bm;
}
//Image Selection End Point
private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
{
// Do nothing it we're not selecting an area.
if (!RegionSelect) return;
RegionSelect = false;
//Display the original image.
pictureBox1.Image = bmpImage;
// Copy the selected part of the image.
int wid = Math.Abs(x0 - x1);
int hgt = Math.Abs(y0 - y1);
if ((wid < 1) || (hgt < 1)) return;
Bitmap area = new Bitmap(wid, hgt);
using (Graphics g = Graphics.FromImage(area))
{
Rectangle source_rectangle = new Rectangle(Math.Min(x0, x1), Math.Min(y0, y1), wid, hgt);
Rectangle dest_rectangle = new Rectangle(0, 0, wid, hgt);
g.DrawImage(bmpImage, dest_rectangle, source_rectangle, GraphicsUnit.Pixel);
}
// Display the result.
pictureBox3.Image = area;
** ERROR occuer here!!!!!**
area.Save(#"C:\Users\Shen\Desktop\LenzOCR\TempFolder\tempPic.jpg"); // error line occcur
singleFileInfo = new FileInfo("C:\\Users\\Shen\\Desktop\\LenzOCR\\TempFolder\\tempPic.jpg");
}
private void ScanBT_Click(object sender, EventArgs e)
{
var folder = #"C:\Users\Shen\Desktop\LenzOCR\LenzOCR\WindowsFormsApplication1\ImageFile";
DirectoryInfo directoryInfo;
FileInfo[] files;
directoryInfo = new DirectoryInfo(folder);
files = directoryInfo.GetFiles("*.jpg", SearchOption.AllDirectories);
var processImagesDelegate = new ProcessImagesDelegate(ProcessImages2);
processImagesDelegate.BeginInvoke(files, null, null);
//BackgroundWorker bw = new BackgroundWorker();
//bw.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
//bw.RunWorkerAsync(bw);
//bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker1_RunWorkerCompleted);
}
private void ProcessImages2(FileInfo[] files)
{
var comparableImages = new List<ComparableImage>();
var index = 0x0;
foreach (var file in files)
{
if (exit)
{
return;
}
var comparableImage = new ComparableImage(file);
comparableImages.Add(comparableImage);
index++;
}
index = 0;
similarityImagesSorted = new List<SimilarityImages>();
var fileImage = new ComparableImage(singleFileInfo);
for (var i = 0; i < comparableImages.Count; i++)
{
if (exit)
return;
var destination = comparableImages[i];
var similarity = fileImage.CalculateSimilarity(destination);
var sim = new SimilarityImages(fileImage, destination, similarity);
similarityImagesSorted.Add(sim);
index++;
}
similarityImagesSorted.Sort();
similarityImagesSorted.Reverse();
similarityImages = new BindingList<SimilarityImages>(similarityImagesSorted);
var buttons =
new List<Button>
{
ScanBT
};
if (similarityImages[0].Similarity > 70)
{
con = new System.Data.SqlClient.SqlConnection();
con.ConnectionString = "Data Source=SHEN-PC\\SQLEXPRESS;Initial Catalog=CharacterImage;Integrated Security=True";
con.Open();
String getFile = "SELECT ImageName, Character FROM CharacterImage WHERE ImageName='" + similarityImages[0].Destination.ToString() + "'";
SqlCommand cmd2 = new SqlCommand(getFile, con);
SqlDataReader rd2 = cmd2.ExecuteReader();
while (rd2.Read())
{
for (int i = 0; i < 1; i++)
{
string getText = rd2["Character"].ToString();
Action showText = () => ocrTB.AppendText(getText);
ocrTB.Invoke(showText);
}
}
con.Close();
}
else
{
MessageBox.Show("No character found!", "Error!", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
#endregion
Since it has been a while, I'm hoping you found your answer, but I'm going to guess that you needed to set the file format when you're saving a jpeg:
area.Save(#"C:\Users\Shen\Desktop\LenzOCR\TempFolder\tempPic.jpg",System.Drawing.Imaging.ImageFormat.Jpeg);
Past that, I can't remember if the picturebox control is double buffered or not which could be the problem (if it's not, you might not be able to access it for saving purposes while it is being rendered, but if you make a copy of area before setting the picturebox3.Image property that would fix that issue):
Bitmap SavingObject=new Bitmap(area);
picturebox3.Image=area;
SavingObject.Save(#"C:\Users\Shen\Desktop\LenzOCR\TempFolder\tempPic.jpg",System.Drawing.Imaging.ImageFormat.Jpeg);
Anyway, I hope you ended up finding your solution (considering it's been a couple months since this was posted).
This looks like a copy of this question:
c# A generic error occurred in GDI+
Same code, same error, same author.
Can't see any difference. But maybe I'm missing something.

Categories