Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Creating a Facebook Like Website Previewer

0.00/5 (No votes)
2 Apr 2012 1  
How to create a Facebook like website previewer user control for Winforms in C#

Introduction

Most of us at one time or another has used Facebook to share a URL link. When you did, you might have noticed that Facebook gives a "preview" of the site contained in the URL. Recently having a need for a user control that does something similar, I tried looking around the Internet for something similar but with no luck. So that is what inspired me to write this user control. The control presented here is a complete multithreaded website previewer, complete with multiple image control.

Using the Control

Before getting into how the control works, I will walk you through how to use it on your own project. All you need to do is reference the DLL. After referencing it, you should see the icon in the Windows Form Designer toolbox. Simply drag the control onto your form, resize it to an appropriate size (I recommend a size of about 400x120). In design mode, the control will not render anything useful.

To use the code, you simply call either the StartRender() method or the StartRenderHtml() method. Here is an example of using StartRender():

htmlSitePreviewer1.StartRender("http://www.ebay.com");

Executing this code will cause the htmlSitePreviewer1 control to begin to render eBay's site. The control is multithreaded and the control immediately returns back to the calling thread. While the control is working in the background to parse the site, a loading animation will render in the control. If you wish to not have a loading animation playing in the background, you can use the following code:

 htmlSitePreviewer1.ShowLoadingStatus = false; 
 htmlSitePreviewer1.StartRender("http://www.ebay.com"); 

 By default, the loading animation is enabled. The sister method, StartRenderHtml() is used very similar. Here is an example:

 string html = "<html><head><title>Test eBay Site</title></head><body><img src=\"http://www.ebay.com/test.png\" /><p>This is a test site for eBay. This is the first paragraph in the test html code.</p></body></html>";
 htmlSitePreviewer1.StartRenderHtml("http://www.ebay.com", html);       

 The StartRenderHtml() method is useful for instances where you are pulling the HTML code from a database rather then from the actual site (perhaps to cache it). To use it, you pass the URL and the HTML code to render. The example above is a very simple example to demonstrate the point. This method is also multithreaded, just like the StartRender() method. 

The HtmlSitePreview control exposes an event called RenderingComplete. This event is fired whenever the control has finished rendering and parsing the site. Here is an example:

htmlSitePreviewer1.RenderingComplete += RenderingDone;
htmlSitePreviewer1.StartRender("http://www.ebay.com");
private void RenderingDone()
{
    Console.WriteLine("Rendering is complete!"); 
} 

 Executing this code will start the rendering process and when its complete, will display "Rendering is complete!" on the console screen.

Prior to rendering, you can also set the UserAgent property string. By setting this string, you can mimic a particular browser when retrieving the website data. By default, this string is set to emulate FireFox running on Windows. Depending on the site, it is possible that different  browsers display a site differently. For example, on mobile phones, websites usually have different html code then desktops. 

Once the rendering has completed, several properties become active. The following properties are available:

Title - This is a read-only property that returns the title of the website

Description - This is a read-only property that returns the description of the website, if there is one.

NumberOfImages - This is a read-only property that returns the number of images found on the website. The HtmlSitePreviewer control will only pick up images on the site that have a minimum width of 50 pixels and a minimum width of 50 pixels. In addition, the images must have a width no bigger then 130 pixels and a height of no bigger then 110 pixels. Finally, the image must have a width to height ratio and height to width ratio of 3.0 or smaller. Any image on the site not matching this criteria will be excluded.

ImageIndex - Finally, this property is used to set the desired image. A valid number would be 1 through NumberOfImages. If set to 0, no image is displayed. The default is 1, which is the first image. If there is no images on the site, then the default is 0.  

How the control works

This section is useful for everyone, especially web developers, to know how the control works.

Upon calling StartRender(), the control will first check to make sure there isn't already a web site in the process of being rendered. If there is, the method simply returns. If the ShowLoadingStatus property is set to true, the control will then call a private method called PrepareLoadingStatus(). This method is shown below:  

        private void PrepareLoadingStatus()
        {
            var panel = new Panel { Dock = DockStyle.Fill, BackColor = Color.White, BorderStyle = BorderStyle.FixedSingle };
            Controls.Add(panel);
            panel.BringToFront();
            var label = new Label
                            {
                                Text = "Loading",
                                AutoSize = false,
                                Width = Width,
                                Height = Height,
                                TextAlign = ContentAlignment.MiddleCenter
                            };
            panel.Controls.Add(label);
            Assembly asm = Assembly.GetExecutingAssembly();
            var backgroundImage = new Bitmap(asm.GetManifestResourceStream("HtmlSitePreviewer.Wait.gif"));
            var pic = new PictureBox
                          {
                              Location = new Point(Width / 2 - 50, Height / 2 - 10),
                              Image = backgroundImage,
                              SizeMode = PictureBoxSizeMode.StretchImage,
                              Width = backgroundImage.Width - 20,
                              Height = backgroundImage.Height - 20
                          };
            panel.Controls.Add(pic);
            pic.BringToFront();
        }

This method created a new Panel control. The Panel control is then brought to the front, in order to be in front of the rendering area and its docked to fill the entire control area of the control. A Label control is then created with the text "Loading" and its centered in the middle of the control area. Finally, a PictureBox control is constructed. This control is also centered and pushed just to the left of the "Loading" text. The PictureBox image is simply an embedded animated gif of a typical wait icon.

Next, the StartRender() method creates a new thread to read the web site's HTML source code. A private event is subscribed to that signals when the thread is finished reading the site's HTML code. The code to read the HTML source is pretty straightforward. It uses HttpWebRequest and HttpWebResponse to fetch the HTML source code of the site.

Once the source code is read, a private method called GetTitle() is executed in order to extract the title of the page. To do this, it simply parses the HTML code and grabs the title between the <title> and </title> tags. The GetTitle() method is shown below:

        private string GetTitle()
        {
            int ndx = _source.ToLower().IndexOf("<title", 0, StringComparison.Ordinal);
            if (ndx >= 0)
            {
                int ndx2 = _source.ToLower().IndexOf(">", ndx, StringComparison.Ordinal);
                if (ndx2 >= 0)
                {
                    ndx2++;
                    int ndx3 = _source.ToLower().IndexOf("</title", ndx2, StringComparison.Ordinal);
                    if (ndx3 >= 0)
                    {
                        return _source.Substring(ndx2, ndx3 - ndx2).Trim(new[] { '\r', '\n', ' ', '\t' });
                    }
                }
            }
            return "";
        }

The method is fairly simple and simply searches for the <title> and </title> tags and simply extracts whatever is between the tags. In the event that the control can't find the opening or closing tag, it returns an empty string.

Once the title has been extracted, the control will then try to get the description. There are two ways it tries to do this. The first is, it attempts to locate a <meta> tag that has an element name of description. If it finds such a meta tag, the content element is read and that is used as the description. If the description can not be extracted through a meta tag, then it attempts to search in the body for the first paragraph <p> tag and if there is text in between the paragraph tags, and its more then 120 characters in length, it will extract and use that as a description. If it can not find this, then it will return an empty string for the description.

Once the title and description are extracted, the images are retrieved. First the control jumps to the <head> section of the HTML source. It then tries to locate a <link> tag with an element of image_src. Any images found in this section are retrieved. Once it scans for <link> tags, it then scans for <img> tags. The images are downloaded and resized (to a proportional width of 100 pixels) and stored in a generic List<Bitmap> collection.

Finally, the actual rendering is to begin. The content of the entire control is filled with a TableLayoutPanel. The TableLayoutPanel is segregated into 2 columns and 3 rows. Here is the code:

            var tlp = new TableLayoutPanel
                          {
                              Location = new Point(0, 0),
                              Name = "TableLayoutPanel1",
                              Dock = DockStyle.Fill,
                              BackColor = Color.White,
                              TabIndex = 0
                          };
            Controls.Add(tlp);
            tlp.RowCount = 3;
            tlp.ColumnCount = 2;
            tlp.RowStyles.Add(new RowStyle(SizeType.Absolute, 14f));
            tlp.RowStyles.Add(new RowStyle(SizeType.Absolute, 18f));
            tlp.RowStyles.Add(new RowStyle(SizeType.Absolute, 12f));
            tlp.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 100f));
            tlp.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100f)); 

 The left column is for the image and is sized to 100 pixels. The right column is sized to 100% of the left over space. Three Label controls are added, one for the url, one for the title and one for the description. A PictureBox is also created to hold the image. These four controls are then added to the TableLayoutPanel and the control is rendered. Finally, it fires off the RenderingComplete event. 

Final Thoughts

I have tested this control with several web sites. Please leave me feedback if there are any problems with the control or any improvements you'd like to see.

History

Version 1.0 -- Initial Release

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here