: C# 2008 Programmer

Creating the Helper Methods

Creating the Helper Methods

When RSS feeds are being downloaded, you want to display a message on the screen to notify the user that the application is downloading the feed (see Figure18-11).


Figure 18-11

For this purpose, you can improvise with the aid of the Panel and Label controls. Define the CreatePanel() function so that you can dynamically create the message panel using a couple of Panel controls and a Label control:

//---create a Panel control to display a message---
private Panel CreatePanel(string str) {
//---background panel---
Panel panel1 = new Panel() {
BackColor = Color.Black,
Location = new Point(52, 13),
Size = new Size(219, 67),
Visible = false,
};
panel1.BringToFront();
//---foreground panel---
Panel panel2 = new Panel() {
BackColor = Color.LightYellow,
Location = new Point(3, 3),
Size = new Size(panel1.Size.Width - 6, panel1.Size.Height - 6)
};
//---add the label to display text---
Label label = new Label() {
Font = new Font(FontFamily.GenericSansSerif, 12, FontStyle.Bold),
TextAlign = ContentAlignment.TopCenter,
Location = new Point(3, 3),
Size = new Size(panel2.Size.Width - 6, panel2.Size.Height - 6),
Text = str
};
//---adds the label to Panel2---
panel2.Controls.Add(label);
//---adds the Panel2 to Panel1---
panel1.Controls.Add(panel2);
return panel1;
}

For simplicity, you are hardcoding the location of panel1 (assuming that this application is running on a wide-screen device). Figure 18-12 shows the various controls forming the display panel.


Figure 18-12

Next, define the IsConnected() function to test whether the user is connected to the Internet:

//---check if you are connected to the Internet---
private bool IsConnected() {
try {
string hostName = Dns.GetHostName();
IPHostEntry curhost = Dns.GetHostEntry(hostName);
return (curhost.AddressList[0].ToString() != IPAddress.Loopback.ToString());
} catch (Exception) {
return false;
}
}

Dns is a static class that provides simple domain name resolution. The GetHostName() method gets the host name of the local computer, which is then passed to the GetHostEntry() method of the Dns class to obtain an IPHostEntry object. IPHostEntry is a container class for Internet host address information. Using this object, you can access its AddressList property to obtain the list of IP addresses associated with it. If the first member of the AddressList property array is not the loopback address (127.0.0.1; represented by IPAddress.Loopback), it is assumed that there is Internet connectivity.

Next, define the DownloadFeed() function, which takes in the URL for the feed you want to download and a title argument (to return the title of the feed). Each post title and its corresponding description is appended to a string and returned to the calling function:

//---download feed and extract Title and Description for each post---
private string DownloadFeed(string feedURL, ref string title) {
XmlDocument xml = new XmlDocument();
//---always load from storage first---
string FileName =
appPath + @"" + RemoveSpecialChars(feedURL) + ".xml";
if (File.Exists(FileName)) {
xml.Load(FileName);
} else {
//---check if there is network connectivity---
if (IsConnected()) {
WebRequest ftpReq = null;
WebResponse ftpResp = null;
Stream ftpRespStream = null;
StreamReader reader = null;
bool getRSSFeedFailed = false;
try {
//---download the RSS document---
ftpReq = WebRequest.Create(feedURL);
ftpResp = ftpReq.GetResponse();
ftpRespStream = ftpResp.GetResponseStream();
reader = new StreamReader(ftpRespStream, System.Text.Encoding.UTF8);
//---load the RSS document into an XMLDocument object---
xml.Load(reader);
//---save a local copy of the feed document---
xml.Save(FileName);
}catch (Exception ex) {
MessageBox.Show(ex.Message);
getRSSFeedFailed = true;
}finally {
if (ftpRespStream != null) {
ftpRespStream.Dispose();
ftpRespStream.Close();
};
if (ftpResp != null) ftpResp.Close();
}
if (getRSSFeedFailed) return String.Empty;
}else {
return String.Empty;
}
}
//---get the title of the feed---
XmlNode titleNode = xml.SelectSingleNode(@"rss/channel/title");
title = titleNode.InnerText;
//---select all <rss><channel><item> elements---
XmlNodeList nodes = xml.SelectNodes("rss/channel/item");
string result = String.Empty;
foreach (XmlNode node in nodes) {
//---select each post's <title> and <description> elements---
result += node.SelectSingleNode("title").InnerText + ((char)3);
result += node.SelectSingleNode("description").InnerText + ((char)12);
}
return result;
}

To download the RSS feed XML documents, you use the WebRequest and WebResponse classes. The document is then read using a StreamReader object and loaded into an XmlDocument object. Each post title and its description are separated by the ASCII character 3, and each posting is separated by the ASCII character 12, like this:

Post_Title<3>Post_Description<12>Post_Title<3>Post_Description<12>
Post_Title<3>Post_Description<12>Post_Title<3>Post_Description<12>
Post_Title<3>Post_Description<12>...

Notice that after the XML feed for an URL is downloaded, it is saved onto storage. This ensures that the application continues to work in offline mode (when user disconnects from the Internet). The URL of the feed is used as the filename, minus all the special characters within the URL, with the .xml extension appended. For example, if the feed URL is http://www.wrox.com/WileyCDA/feed/RSS_WROX_ ALLNEW.xml, then the filename would be httpwwwwroxcomWileyCDAfeedRSSWROXALLNEWxml.xml. To strip off all the special characters in the URL, define the RemoveSpecialChars() function as follows:

//---removes special chars from an URL string---
private string RemoveSpecialChars(string str) {
string NewString = String.Empty;
Regex reg = new Regex("[A-Z]|[a-z]");
MatchCollection coll = reg.Matches(str);
for (int i = 0; i <= coll.Count - 1; i++)
NewString = NewString + coll[i].Value;
return NewString;
}

You use the Regex (regular expression) class to extract all the alphabets from the URL and append them into a string, which will be returned to the calling function to use as a filename.

Next, define the SubscribeFeed() function to subscribe to a feed, and then add each post to the TreeView control (see Figure 18-13):

//---returns true if subscription is successful---
private bool SubscribeFeed(string URL) {
bool succeed = false;
try {
//---display the wait message panel---
if (displayPanel == null) {
displayPanel = CreatePanel("Downloading feed...Please wait.");
this.Controls.Add(displayPanel);
} else {
displayPanel.BringToFront();
displayPanel.Visible = true;
Cursor.Current = Cursors.WaitCursor;
//---update the UI---
Application.DoEvents();
}
//---download feed---
string title = String.Empty;
string[] posts = DownloadFeed(URL, ref title).Split((char)12);
if (posts.Length > 0 && posts[0] != String.Empty) {
//---always add to the root node---
TreeNode FeedTitleNode = new TreeNode() {
Text = title,
Tag = URL, //---stores the Feed URL---
ImageIndex = ICO_CLOSE,
SelectedImageIndex = ICO_OPEN
};
//---add the feed title---
TreeView1.Nodes[0].Nodes.Add(FeedTitleNode);
//---add individual elements (posts)---
for (int i = 0; i <= posts.Length - 2; i++) {
//---extract each post as "title:description"---
string[] str = posts[i].Split((char)3);
TreeNode PostNode = new TreeNode() {
Text = str[0], //---title---
Tag = str[1], //---description---
ImageIndex = ICO_POST,
SelectedImageIndex = ICO_POST
};
//---add the posts to the tree---
TreeView1.Nodes[0].Nodes[TreeView1.Nodes[0].Nodes.Count - 1].Nodes.Add(PostNode);
}
//---subscription is successful---
succeed = true;
//---highlight the new feed and expand its post---
TreeView1.SelectedNode = FeedTitleNode;
} else succeed = false;
} catch (Exception ex) {
MessageBox.Show(ex.Message);
//---subscription is not successful---
succeed = false;
} finally {
//---clears the panel and cursor---
Cursor.Current = Cursors.Default;
displayPanel.Visible = false;
//---update the UI---
Application.DoEvents();
}
return succeed;
}


Figure 18-13

For each TreeView node representing a feed title (such as Wrox: All New Titles), the Text property is set to the feed's title and its URL is stored in the Tag property of the node. For each node representing a posting (.NET Domain-Driven Design and so forth), the Text property is set to the posting's title and its description is stored in the Tag property.


: 1.069. /Cache: 3 / 1