65.9K
CodeProject 正在变化。 阅读更多。
Home

创建类似 Facebook 的网站预览器

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.50/5 (8投票s)

2012年4月2日

CPOL

6分钟阅读

viewsIcon

44332

downloadIcon

1520

如何在 C# 中为 Winforms 创建类似 Facebook 的网站预览器用户控件

引言

我们大多数人在某个时候都使用过Facebook来分享一个URL链接。当你这样做时,你可能注意到Facebook会提供一个URL中包含的网站的“预览”。最近,我需要一个做类似事情的用户控件,但在网上搜寻了一番,却一无所获。这就是激发我编写这个用户控件的灵感。这里提供的控件是一个完整的多线程网站预览器,并附带多个图像控件。

使用控件

在深入探讨控件的工作原理之前,我将带你了解如何在自己的项目中使用它。你只需要引用DLL文件。引用后,你应该会在Windows窗体设计器工具箱中看到该图标。只需将控件拖到你的窗体上,然后调整到合适的大小(我推荐大约400x120的大小)。在设计模式下,控件不会渲染任何有用的东西。

要使用代码,只需调用StartRender()方法或StartRenderHtml() 方法即可。下面是使用StartRender()的示例:

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

执行此代码将导致htmlSitePreviewer1 控件开始渲染eBay的网站。该控件是多线程的,并且控件会立即返回到调用线程。当控件在后台解析网站时,一个*加载*动画会在控件中渲染。如果你不想在后台播放*加载*动画,可以使用以下代码:

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

 默认情况下,*加载*动画是启用的。姊妹方法StartRenderHtml() 的用法也非常相似。下面是一个示例:

 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);       

 StartRenderHtml()方法适用于从数据库中提取HTML代码而不是从实际网站提取(也许是为了缓存)的情况。要使用它,你需要传递URL和要渲染的HTML代码。上面的示例是一个非常简单的演示。此方法也与StartRender()方法一样是多线程的。 

HtmlSitePreview控件公开了一个名为RenderingComplete的事件。每当控件完成渲染和解析网站时,就会触发此事件。下面是一个示例:

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

 执行此代码将启动渲染过程,完成后,将在控制台屏幕上显示“渲染完成!”。

在渲染之前,你还可以设置UserAgent属性字符串。通过设置此字符串,你可以在检索网站数据时模拟特定的浏览器。默认情况下,此字符串设置为模拟在Windows上运行的FireFox。根据不同的网站,不同的浏览器可能会以不同的方式显示网站。例如,在手机上,网站通常比桌面端有不同的HTML代码。 

渲染完成后,将激活几个属性。以下属性可用:

Title - 这是一个只读属性,返回网站的标题

Description - 这是一个只读属性,返回网站的描述(如果存在)。

NumberOfImages - 这是一个只读属性,返回网站上找到的图像数量。HtmlSitePreviewer控件只会抓取网站上宽度最小为50像素且高度最小为50像素的图像。此外,图像的宽度不得大于130像素,高度不得大于110像素。最后,图像的宽度与高度之比以及高度与宽度之比必须小于或等于3.0。不符合此标准的任何图像都将被排除。

ImageIndex - 最后,此属性用于设置所需的图像。有效数字为1到NumberOfImages。如果设置为0,则不显示任何图像。默认值为1,即第一张图像。如果网站上没有图像,则默认值为0。  

控件的工作原理

这一节对所有人都有用,特别是Web开发人员,可以了解控件的工作原理。

调用StartRender()后,控件首先会检查当前是否正在渲染一个网站。如果正在进行,该方法将直接返回。如果ShowLoadingStatus属性设置为true,控件将调用一个私有方法PrepareLoadingStatus()。该方法如下所示:  

        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();
        }

此方法创建了一个新的Panel控件。然后将Panel控件置于最前面,以便位于渲染区域的前面,并将其停靠以填充控件的整个区域。然后创建一个Label控件,文本为“Loading”,并将其居中放在控件区域的中间。最后,构造一个PictureBox控件。此控件也居中放置,并稍微移到“Loading”文本的左侧。PictureBox图像只是一个嵌入式的动画GIF,通常是等待图标。

接下来,StartRender()方法创建一个新线程来读取网站的HTML源代码。订阅了一个私有事件,该事件会在线程完成读取网站HTML代码时发出信号。读取HTML源的代码非常简单。它使用HttpWebRequestHttpWebResponse来获取网站的HTML源代码。

源代码读取完成后,将执行一个私有方法GetTitle()来提取页面的标题。为此,它只需解析HTML代码,并提取<title></title>标签之间的标题。GetTitle()方法如下所示:

        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 "";
        }

该方法相当简单,只需搜索<title></title>标签,然后提取标签之间的任何内容。 如果控件找不到开始或结束标签,则返回空字符串。

提取标题后,控件将尝试获取描述。它通过两种方式尝试执行此操作。第一种是,它尝试定位一个名为description<meta>标签。如果找到这样的meta标签,则读取content元素,并将其用作描述。如果无法通过meta标签提取描述,则它会尝试在正文中搜索第一个段落<p>标签,如果段落标签之间有文本,并且长度超过120个字符,则将其提取并用作描述。如果找不到,则返回空字符串作为描述。

提取标题和描述后,将检索图像。首先,控件跳转到HTML源的<head>部分。然后它尝试定位一个名为image_src<link>标签。在此部分找到的任何图像都将被检索。扫描完<link>标签后,它会扫描<img>标签。图像将被下载并调整大小(成比例宽度为100像素),并存储在通用的List<Bitmap>集合中。

最后,实际渲染即将开始。整个控件的内容填充了一个TableLayoutPanel。TableLayoutPanel被划分为2列和3行。这是代码:

            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)); 

 左列用于图像,尺寸为100像素。右列的尺寸为左侧剩余空间的100%。添加了三个Label控件,一个用于URL,一个用于标题,一个用于描述。还创建了一个PictureBox来保存图像。然后将这四个控件添加到TableLayoutPanel中,并渲染控件。最后,它触发RenderingComplete事件。 

最终想法

我已经用几个网站测试了这个控件。如果控件有任何问题或您希望看到任何改进,请给我反馈。

历史

版本1.0 -- 初始发布

© . All rights reserved.