使用 BandObject 的 RSS bar






4.14/5 (4投票s)
2005 年 5 月 14 日
4分钟阅读

68977

842
IE/Explorer 工具栏中的 RSS 聚合器。
引言
我经常在城市里的建筑墙壁上看到闪烁的新闻展示,新闻标题一行接一行地滚动。在网上,我们也能看到同样的东西吗?每天有成百上千的 RSS 网站提供成千上万条新闻。如果我们能在 IE/Explorer 的工具栏上看到滚动的标题,并且只需点击一下就能阅读它们的详细信息,那不是很酷吗?所以我就做了这个。RSSbar 是一个工具栏上的 RSS 聚合器。
背景
我使用了 Pavel Zolnikov 的 BandObject
代码来制作工具栏,它非常易于使用。它真正让我从复杂的 COM 编程中解放出来。非常感谢这么棒的代码!
首次设置
安装演示项目 Setup.msi 后,可以通过 IE/Explorer 菜单中的“查看->探索栏”和“查看->工具栏”来使用 RSSbar。点击“RSSbar”菜单项,一个手柄会出现在现有工具栏的右侧边缘。您可以拖动手柄来设置位置和长度,以便可以看到每个新闻标题。然后点击左侧的手柄,在下拉菜单中选择“添加”来注册 RSS 订阅的 URL。在下面的对话框中注册 URL 后,RSS 订阅的顶部标题将出现在下拉菜单中。好了吗?然后只需点击您想查看的任何标题即可。
自定义集合类
RSSbar 的主要逻辑很简单。读取 RSS 订阅的 XML 文件后,它会动态创建足够多的 LinkLabel
来显示新闻标题,将它们放在 RSSbar 的面板上,并以恒定的速度通过计时器移动。为了做到这一点,我创建了一个自定义集合类 LinkLabalArray
,它管理 LinkLabel
的集合,并且还处理每个 LinkLabel
的点击事件;这意味着通过 BandObject
的 IWebBrowser2
接口的 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
类,它调用 CreateActCtx
和 ActivateActCtx
Win32 函数,而不是使用最终失败的“.dll.manifest”文件。非常聪明!感谢匿名的贡献者!
历史
- 初版 - 2005 年 5 月 14 日。