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

使用 BandObject 的 RSS bar

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.14/5 (4投票s)

2005 年 5 月 14 日

4分钟阅读

viewsIcon

68977

downloadIcon

842

IE/Explorer 工具栏中的 RSS 聚合器。

Sample Image - RSSbar1.gif

引言

我经常在城市里的建筑墙壁上看到闪烁的新闻展示,新闻标题一行接一行地滚动。在网上,我们也能看到同样的东西吗?每天有成百上千的 RSS 网站提供成千上万条新闻。如果我们能在 IE/Explorer 的工具栏上看到滚动的标题,并且只需点击一下就能阅读它们的详细信息,那不是很酷吗?所以我就做了这个。RSSbar 是一个工具栏上的 RSS 聚合器。

背景

我使用了 Pavel Zolnikov 的 BandObject 代码来制作工具栏,它非常易于使用。它真正让我从复杂的 COM 编程中解放出来。非常感谢这么棒的代码!

首次设置

安装演示项目 Setup.msi 后,可以通过 IE/Explorer 菜单中的“查看->探索栏”和“查看->工具栏”来使用 RSSbar。点击“RSSbar”菜单项,一个手柄会出现在现有工具栏的右侧边缘。您可以拖动手柄来设置位置和长度,以便可以看到每个新闻标题。然后点击左侧的手柄,在下拉菜单中选择“添加”来注册 RSS 订阅的 URL。在下面的对话框中注册 URL 后,RSS 订阅的顶部标题将出现在下拉菜单中。好了吗?然后只需点击您想查看的任何标题即可。

Sample Image - RSSbar2.gif

自定义集合类

RSSbar 的主要逻辑很简单。读取 RSS 订阅的 XML 文件后,它会动态创建足够多的 LinkLabel 来显示新闻标题,将它们放在 RSSbar 的面板上,并以恒定的速度通过计时器移动。为了做到这一点,我创建了一个自定义集合类 LinkLabalArray,它管理 LinkLabel 的集合,并且还处理每个 LinkLabel 的点击事件;这意味着通过 BandObjectIWebBrowser2 接口的 Navigate 方法跳转到每个标题的详细页面。maxlblWidth 属性获取集合中最长标题的宽度。稍后我将说明为什么需要这个。

public class LinkLabelArray : CollectionBase
{
    private readonly UserControl hostControl;
    private WebBrowserClass webControl;
    private static bool bFirst = true;

    public LinkLabelArray(UserControl host)
    {
        hostControl = host; //RSSbar instance
    }


    public void AddNewLinkLabel()
    {
        LinkLabel aLabel = new LinkLabel();
        this.List.Add(aLabel);
        ((RSSBar)hostControl).panel1.Controls.Add(aLabel);

        //set linklabel properties
        aLabel.AutoSize = true;
        aLabel.LinkVisited = true;
        aLabel.Visible = true;
        aLabel.Top = 5;
        aLabel.Height = 15;
        aLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
        // handler for clicked event of news title label
        aLabel.LinkClicked += new LinkLabelLinkClickedEventHandler(ClickHandler);
    }

    public LinkLabel this [int Index]
    {
        get
        {
            return (LinkLabel) this.List[Index];
        }
    }

    public int maxlblWidth //get the length of the longest news title
    {
        get
        {
            int max = 0;
            IEnumerator myEnum = this.GetEnumerator();

            while(myEnum.MoveNext())
            {
                if(((LinkLabel)myEnum.Current).Width > max)
                max = ((LinkLabel)myEnum.Current).Width;
            }
            return max;
        }
    }

    public new void Clear()
    {
        ((RSSBar)hostControl).panel1.Controls.Clear();
        this.List.Clear();
    }

    public void ClickHandler(Object sender, LinkLabelLinkClickedEventArgs e)
    {
        if(bFirst)
        //get the instance of explorer only once for the first clicked event
        {
            webControl = ((RSSBar)hostControl).getExplorer; 
            bFirst = false;
        }
        object Null = System.Reflection.Missing.Value;
        ((LinkLabel)sender).Links[0].Visited = true;
        // navigate to the url of the clicked linklabel
        ((IWebBrowser2)webControl).Navigate(e.Link.LinkData.ToString(),
                                  ref Null,ref Null,ref Null,ref Null);
    }        
}

我还创建了另一个集合类 MenuItemArray,用于在用户注册 RSS 订阅 URL 后动态添加下拉菜单项。

读取 RSS 订阅的 XML

要创建 LinkLabel 集合,我们必须读取 RSS 订阅的 XML 文件。这是 RSSbar 类中 rssRead 方法的工作。首先,它会检查 XML 的格式,如果是有效的 RSS 订阅,则获取包含我们感兴趣的新闻标题和链接的“item”节点列表。之后,它会创建 LinkLabel 集合 LinkLabelArray,为每个节点创建一个 LinkLavel,为每个 LinkLabel 设置标题和链接,同时设置每个标签在面板上的显示位置,紧跟在前一个标签后面。就是这样。

public void rssRead(string Url)
{
    this.panel1.Width = defaultPanelWidth; //reset panel length
    XmlDocument xmlDoc= new XmlDocument();

    // check the url if it is Rss feed or not
    try
    {
        xmlDoc.Load(Url);
    }
    catch(XmlException)
    {
        MessageBox.Show("Not RSS feed!!");
        return;
    }
    XmlElement root = xmlDoc.DocumentElement;
    if(root.Name != "rss" && root.Name != "rdf:RDF")
    {
        MessageBox.Show("Not RSS feed!!");
        return;
    }

    // collect item nodes, each item contains news title and link node
    XmlNodeList itemList = root.GetElementsByTagName("item");

    if(itemList.Count != 0)
    {
        // set number of the news titles to display
        int itemCount;
        if(itemList.Count < maxItems)
        {
            itemCount = itemList.Count;
        }
        else
            itemCount = maxItems;

        int dispLeft = 0;
        // this variable holds total length of news titles at the end of loop

        for (int i=0;i < itemCount; i++)
        {
            XmlNode item = itemList[i]; //each item node
            lblArray.AddNewLinkLabel(); // making new title label

            for (int j=0; j < item.ChildNodes.Count; j++)
            {
                XmlNode chld = item.ChildNodes[j];
                // we are interested only in the title and link node
                switch (chld.Name)
                {
                    case "title":
                    {
                        lblArray[i].Text = chld.InnerText;
                        break;
                    }
                    case "link":
                    {
                        lblArray[i].Links.Add(0,lblArray[i].Text.Length, 
                                                        chld.InnerText);
                        break;
                    }
                }                        
            }
            //set display position of each title label in the panel
            lblArray[i].Left = dispLeft;
            //set position just behind the previous title
            dispLeft += lblArray[i].Width;
        }
        // panel length <= total length
        //    of titles - length of the longest title (cyclic formula)
        if(this.panel1.Width > dispLeft - lblArray.maxlblWidth)
            this.panel1.Width = dispLeft - lblArray.maxlblWidth;
    }
}

循环公式

在制作 RSSbar 时,有一个关键问题需要解决。为了在工具栏上连续无缝地循环显示新闻标题,我只是在最后一个标题完全出现在面板右边缘时,就将其位置重置到最后一个标题的尾部,然后将第二个标题重置到第一个标题的尾部,以此类推,就像这样

// moving news title labels on each timer tick 
private void timer1_Tick(object sender, System.EventArgs e)
{
    for(int i = 0; i < lblArray.Count; i++)
    {
        lblArray[i].Left -= 1; // move to the left 1 pixel per tick

        // each title cycling on panel from right to left 
        if(lblArray[i==0 ? lblArray.Count-1:i-1].Right == 
           this.panel1.Width && lblArray[i].Right < 0)
        {
            lblArray[i].Left = this.panel1.Width;
        }

    }
}

这效果很好,只要新闻标题的总长度足够长,比面板长度长。否则,这个功能就会失败,因为我不想复制同一个标题的 LinkLabel 来填充面板。结果,每个标题之间的间隔变得远远不恒定,看起来很丑。经过几次尝试,我想到一个改变面板长度的办法,使其根据标题的总长度进行调整。这就是我找到的“循环公式”

panel length <= total length of titles - length of the longest title

这个公式在前面提到的 rssRead 方法的末尾使用,通过 LinkLabelArray 类的 maxlblWidth 属性来正确设置面板长度。

应用 XP 风格

为了将 XP 风格(Visual Style)应用到工具栏按钮和对话框,我使用了在 BandObject 文章的 FAQ 消息中找到的 EnableVS 类,它调用 CreateActCtxActivateActCtx Win32 函数,而不是使用最终失败的“.dll.manifest”文件。非常聪明!感谢匿名的贡献者!

历史

  • 初版 - 2005 年 5 月 14 日。
© . All rights reserved.