建立一個 Windows Forms 應用程式,輸入 RSS 連結,解析 XML 內容,以樹狀結構顯示頻道標題與新聞項目,點選新聞標題自動開啟瀏覽器跳轉原文連結。
標準 RSS 2.0 架構:
<rss version="2.0">
<channel>
<title>頻道名稱</title>
<link>頻道連結</link>
<description>頻道描述</description>
<item>
<title>新聞標題</title>
<link>新聞連結</link>
<description>新聞摘要</description>
<pubDate>發布時間</pubDate>
</item>
... 多個 item ...
</channel>
</rss>
public class RssFeed
{
public Channel Channel { get; set; }
}
public class Channel
{
public string Title { get; set; }
public string Link { get; set; }
public string Description { get; set; }
public List<Item> Items { get; set; } = new List<Item>();
}
public class Item
{
public string Title { get; set; }
public string Link { get; set; }
public string Description { get; set; }
public DateTime PubDate { get; set; }
}
採用強型別類別取代原文 struct + Hashtable,支援 LINQ 查詢與資料繫結。
TextBox txtUrl:輸入 RSS 連結Button btnLoad:觸發載入TreeView tvRss:顯示頻道與新聞階層WebBrowser / Label:(選用) 顯示新聞摘要private RssFeed LoadRssFeed(string url)
{
var feed = new RssFeed();
var doc = new XmlDocument();
doc.Load(url); // 直接支援 http/https
XmlNode channelNode = doc.SelectSingleNode("/rss/channel");
if (channelNode == null) throw new Exception("無效的 RSS 格式");
feed.Channel = new Channel
{
Title = channelNode.SelectSingleNode("title")?.InnerText ?? "",
Link = channelNode.SelectSingleNode("link")?.InnerText ?? "",
Description = channelNode.SelectSingleNode("description")?.InnerText ?? ""
};
foreach (XmlNode itemNode in channelNode.SelectNodes("item"))
{
feed.Channel.Items.Add(new Item
{
Title = itemNode.SelectSingleNode("title")?.InnerText ?? "",
Link = itemNode.SelectSingleNode("link")?.InnerText ?? "",
Description = itemNode.SelectSingleNode("description")?.InnerText ?? "",
PubDate = DateTime.TryParse(itemNode.SelectSingleNode("pubDate")?.InnerText, out var dt) ? dt : DateTime.MinValue
});
}
return feed;
}
XmlDocument.Load(string) 直接支援 URL,內部自動處理 HTTP 請求。SelectSingleNode/SelectNodes 使用 XPath,程式碼簡潔且容錯 (節點不存在回傳 null)。private void BindToTreeView(RssFeed feed)
{
tvRss.BeginUpdate();
tvRss.Nodes.Clear();
var channelNode = new TreeNode(feed.Channel.Title) { Tag = feed.Channel.Link };
tvRss.Nodes.Add(channelNode);
foreach (var item in feed.Channel.Items)
{
var itemNode = new TreeNode(item.Title)
{
Tag = item.Link, // 儲存連結供點選時使用
ToolTipText = item.Description // 滑鼠停留顯示摘要
};
channelNode.Nodes.Add(itemNode);
}
channelNode.ExpandAll();
tvRss.EndUpdate();
}
private void tvRss_AfterSelect(object sender, TreeViewEventArgs e)
{
if (e.Node.Tag is string url && Uri.IsWellFormedUriString(url, UriKind.Absolute))
{
try { Process.Start(new ProcessStartInfo(url) { UseShellExecute = true }); }
catch (Exception ex) { MessageBox.Show($"無法開啟連結: {ex.Message}"); }
}
}
UseShellExecute = true 確保使用系統預設瀏覽器,支援 .NET Core / .NET 5+。
public partial class MainForm : Form
{
public MainForm() { InitializeComponent(); }
private async void btnLoad_Click(object sender, EventArgs e)
{
string url = txtUrl.Text.Trim();
if (string.IsNullOrEmpty(url)) return;
btnLoad.Enabled = false;
Cursor = Cursors.WaitCursor;
try
{
// 非同步載入避免 UI 凍結
var feed = await Task.Run(() => LoadRssFeed(url));
BindToTreeView(feed);
}
catch (Exception ex)
{
MessageBox.Show($"載入失敗: {ex.Message}", "錯誤", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
finally
{
btnLoad.Enabled = true;
Cursor = Cursors.Default;
}
}
// ... LoadRssFeed, BindToTreeView, tvRss_AfterSelect 方法
}
| 問題 | 原因 | 解決 |
|---|---|---|
| 載入時 UI 凍結 | 同步網路 I/O 阻塞 UI 執行緒 | 使用 Task.Run + await 非同步載入 |
| 中文顯示亂碼 | RSS 未宣告 encoding 或編碼不符 | XmlDocument 自動偵測 BOM/宣告;若失敗可先 HttpClient 下載字串指定 Encoding.UTF8 再 LoadXml |
| 部分節點缺失 | RSS 格式不標準 (如 Atom、RDF) | 擴充解析邏輯支援多種命名空間,或改用 SyndicationFeed (.NET Framework 內建) |
| HTTPS 憑證錯誤 | 伺服器憑證不受信任 | 開發環境可暫時忽略:ServicePointManager.ServerCertificateValidationCallback = (a,b,c,d) => true; |
ContextMenuStrip:右鍵複製連結、標記已讀、匯出 OPML。WebView2 控制項:應用程式內直接瀏覽全文。SQLite 或 JSON 儲存已讀狀態與離線閱讀。Timer 背景檢查新文章並通知 (Toast/系統匣)。