建立一個 Windows Forms 應用,輸入 RSS 連結,解析 XML 結構,以樹狀檢視顯示頻道標題與新聞項目,點擊新聞標題以預設瀏覽器開啟原文連結。
<rss version="2.0">
<channel>
<title>頻道名稱</title>
<link>頻道網址</link>
<description>頻道描述</description>
<item>
<title>新聞標題</title>
<link>新聞網址</link>
<description>摘要內容</description>
<pubDate>發布時間</pubDate>
</item>
... 多個 item ...
</channel>
</rss>
核心節點:`rss` → `channel` → 多個 `item`(各含 `title`, `link`, `description` 等)。
public class RssFeed
{
public Channel Channel { get; set; } = new();
public class Channel
{
public string Title { get; set; } = "";
public string Link { get; set; } = "";
public string Description { get; set; } = "";
public List- Items { get; set; } = new();
}
public class Item
{
public string Title { get; set; } = "";
public string Link { get; set; } = "";
public string Description { get; set; } = "";
public DateTime PubDate { get; set; }
}
}
using System.Xml;
public static RssFeed ParseRss(string url)
{
var feed = new RssFeed();
var doc = new XmlDocument();
// 設定逾時與安全性
var request = (HttpWebRequest)WebRequest.Create(url);
request.Timeout = 10000;
request.UserAgent = "RssReader/1.0";
using (var response = (HttpWebResponse)request.GetResponse())
using (var stream = response.GetResponseStream())
{
doc.Load(stream); // 直接從串流載入,不落地檔案
}
// 找 channel 節點
XmlNode channelNode = doc.SelectSingleNode("/rss/channel");
if (channelNode == null) throw new XmlException("無效的 RSS 格式:找不到 channel");
// 讀取 channel 基本資訊
feed.Channel.Title = channelNode.SelectSingleNode("title")?.InnerText ?? "";
feed.Channel.Link = channelNode.SelectSingleNode("link")?.InnerText ?? "";
feed.Channel.Description = channelNode.SelectSingleNode("description")?.InnerText ?? "";
// 遍歷所有 item
XmlNodeList itemNodes = channelNode.SelectNodes("item");
foreach (XmlNode itemNode in itemNodes)
{
var item = new RssFeed.Item
{
Title = itemNode.SelectSingleNode("title")?.InnerText ?? "(無標題)",
Link = itemNode.SelectSingleNode("link")?.InnerText ?? "",
Description = itemNode.SelectSingleNode("description")?.InnerText ?? "",
};
// 解析發布日期(RFC 822 / ISO 8601 格式兼容)
string dateStr = itemNode.SelectSingleNode("pubDate")?.InnerText;
if (DateTime.TryParse(dateStr, out DateTime dt))
item.PubDate = dt;
feed.Channel.Items.Add(item);
}
return feed;
}
private async void btnLoad_Click(object sender, EventArgs e)
{
btnLoad.Enabled = false;
tvFeed.Nodes.Clear();
try
{
// 非同步載入避免 UI 凍結
var feed = await Task.Run(() => ParseRss(txtUrl.Text.Trim()));
// 建立樹狀節點
var channelNode = new TreeNode(feed.Channel.Title) { Tag = feed.Channel.Link };
tvFeed.Nodes.Add(channelNode);
foreach (var item in feed.Channel.Items)
{
var itemNode = new TreeNode(item.Title) { Tag = item.Link };
// 可選:ToolTip 顯示摘要/日期
itemNode.ToolTipText = $"{item.PubDate:yyyy-MM-dd HH:mm}\n{item.Description}";
channelNode.Nodes.Add(itemNode);
}
channelNode.ExpandAll();
}
catch (Exception ex)
{
MessageBox.Show($"載入失敗:{ex.Message}", "錯誤", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
finally { btnLoad.Enabled = true; }
}
private void tvFeed_AfterSelect(object sender, TreeViewEventArgs e)
{
if (e.Node.Tag is string url && !string.IsNullOrWhiteSpace(url))
{
try
{
// 使用 Process.Start 開啟預設瀏覽器 (.NET Core 3.0+ 需 UseShellExecute=true)
var psi = new ProcessStartInfo(url) { UseShellExecute = true };
Process.Start(psi);
}
catch (Exception ex)
{
MessageBox.Show($"無法開啟連結:{ex.Message}");
}
}
}
| 功能 | 實作建議 |
|---|---|
| 非同步增量載入 | 大量項目時分批 `BeginUpdate/EndUpdate` 或虛擬模式 |
| 內容預覽 | 選中項目時在 `WebView2` 或 `RichTextBox` 顯示 `Description` (HTML 清理) |
| 已讀/未讀標記 | 本地 SQLite / JSON 儲存已讀項目 GUID/Link,節點圖示區分 |
| 定時自動重新整理 | `System.Threading.Timer` 背景輪詢,更新節點文字顏色提示新文章 |
| 支援 Atom 格式 | 擴充解析器偵測 `feed` 根節點,對應 `entry`、`link[@rel='alternate']` |
| OPML 匯入/匯出 | 管理多組 RSS 訂閱清單 |
利用 `XmlDocument` + XPath 解析 RSS 結構直觀且穩健,搭配 `TreeView` 呈現階層資料、`Process.Start` 導航外部連結,即可快速構建實用的新聞閱讀器原型。後續可擴充為支援分類、標籤、離線閱讀、全文搜尋的完整資訊聚合工具。