TL;DR:
We’re getting there. In this one, we build the Feed class and the FeedsManager class that manages all of the feeds the user wants to view in the Aggregator.
Code is here, video is here (and down below)
The Brain of the Aggregator
Like I said above, we’re gonna talk about the Feed class and the FeedsManager class. We could really call the FeedsManager the brain of the application. It’s what handles saving, editing, and deleting feeds that the user wants/doesn’t want to view.
and….
I also recommend that you check out this post about using the ConfigurationManager
class.
The Feed class:
The purpose of the Feed
class is to store all of the information about a feed and let us load it when we’re ready to display it. I wrote it this way for two reasons. One is that we don’t want to be sending unnecessary web requests by loading the feed immediately when it’s created. Since the creation of the feed is handled in a separate tab page, we would never see any of the data at that point anyway. Two, I got temporarily blocked on one of the NewEgg feeds because I was sending too many requests to their servers while I was testing things out… D’oh!
The Code:
This is it:
namespace Aggregator_App.Classes { class Feed { public string Name { get; set; } public string URL { get; set; } public Channel Channel { get; set; } public List Items { get; set; } public string LastBuildDate { get; set; } private Parser RSSParser { get; set; } public Feed(string name, string url) { Name = name; URL = url; } public void LoadFeed() { try { RSSParser = new Parser(URL); } catch (Exception) { throw new InvalidFeedException("Feed URL was not valid"); } MapToParser(); } public override string ToString() { return Name; } public void UpdateFeed() { LoadFeed(); } private void MapToParser() { Channel = RSSParser.RSSChannel; Items = RSSParser.Items; LastBuildDate = RSSParser.RSSChannel.BuildDate; } } }
The Properties:
As you can see, we’ve got a few properties there. Name
and URL
store the required information about the Feed when we create it and the other properties (excluding the RSSParser
) just mimic the XMLParser
class we created earlier in this series.
The Methods:
We’ve got a LoadFeed()
function that creates the RSSParser
object, an overridden ToString()
method so that we can view each instance in a more meaningful way if we need to troubleshoot, an UpdateFeed()
function, and a MapToParser()
function which maps the RSSParser
properties to the Feed
properties when it is loaded.
Refresher:
If you don’t remember much about the XMLParser class, that’s ok. Here’s the post about it, and I’ve included the code as well:
public class Parser { public Channel RSSChannel { get; set; } public List Items { get; set; } private XDocument doc; public Parser(string RSSFeedURL) { ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11; this.doc = XDocument.Load(RSSFeedURL); this.RSSChannel = this.CreateChannel(); this.Items = this.CreateItems(); } private Channel CreateChannel() { string title = (from c in doc.Root.Descendants("channel") select c.Element("title").Value).FirstOrDefault(); string link = (from c in doc.Root.Descendants("channel") select c.Element("link").Value).FirstOrDefault(); string description = (from c in doc.Root.Descendants("channel") select c.Element("description").Value).FirstOrDefault(); string buildDate; try { buildDate = (from c in doc.Root.Descendants("channel") select c.Element("lastBuildDate").Value).FirstOrDefault(); } catch (Exception ex) { buildDate = "Not Available"; } return new Channel(title, link, description, buildDate); } private List CreateItems() { List itemTags = doc.Root.Element("channel").Elements("item").ToList(); List items = new List(); foreach (XElement item in itemTags) { string title = item.Element("title").Value; string link = item.Element("link").Value; string description = item.Element("description").Value; string modifiedDescription = description.Replace("src=\"//", "src=\"http://"); string pubDate = item.Element("pubDate").Value; items.Add(new Item(title, link, modifiedDescription, pubDate)); } return items; } public override string ToString() { string output = ""; output += this.RSSChannel.ToString(); foreach(Item item in this.Items) { output += item.ToString(); } return output; } }
The FeedsManager class:
Next, we have the FeedsManager class. This class specifically handles only the configuration of the feeds. Ya know, adding a feed, modifying a feed link, and deleting them. It accomplishes this by using what’s called the ConfigurationManager
. I don’t want to spend too much time on this here because there’s a lot of information to cover and I’ve already got a post about it here. Long story short, the ConfigurationManager
is part of the .NET framework that you can use to create an XML based configuration file that your app can interact with. Again, go check out this post to see how it all works. You can find all of the code for that particular project here as well.
I do need to point out a slight change though. When I created the FeedsManager
class, I used a Feeds
property and initiated that property in the constructor of the class. This is unnecessary so I changed it. Anytime we need to get the Feeds from the FeedsManager
, we can just call the GetFeedsFromConfig()
method. Yea, I screwed up, sorry guys.
What about the rest?
Great question. The rest of the video shows how we use the classes we just talked about. All we really have to do is hook up the button events to call the corresponding methods of the FeedsManager
class. There is some logic there too, but nothing super complicated. Watch the video, dig into the code, and if you’ve got questions, just ask them somewhere (here, or on the YouTube video page) and I’ll be glad to clear them up!
Wait…I feel bad. Here’s one for free:
private void btn_delete_selected_feed_Click(object sender, EventArgs e) { string prompt = "Are you sure you want to delete the selected feed(s)?"; if (ltb_feeds.SelectedItems.Count > 0 && MetroMessageBox.Show(this, prompt, "Delete Feed", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question) == DialogResult.Yes) { for (int i = ltb_feeds.SelectedItems.Count - 1; i >= 0; i--) { try { feedsManager.RemoveFeedFromConfig(((Feed)ltb_feeds.SelectedItems[i]).Name); RemoveItemFromListBox(ltb_feeds, (Feed)ltb_feeds.SelectedItems[i]); } catch (ConfigurationErrorsException ex) { Alert(ex.Message); continue; } } } }
This is the function we’ll call when the delete button is pressed. So first we’re going to make sure that there were some items selected in our ListBox
. Since we’re using the MultiExtended
option for the SelectionMode
property, we can have multiple items selected too. Then we’re going to show a MetroMessageBox
to the user and only perform the delete if they hit the Yes button. Then we’re going to…
- Loop through all of the selected items, in a descending fashion since we’re removing items. In case you didn’t know, when you’re removing items from something, that changes the count. Therefore, if you’re using that count to perform the loop, it will get out of whack and you won’t actually remove all the items. Don’t believe me? Test it out and see.
- Try to remove each feed from the
feedsManager
. We’ll have to do some casting to get thatltb_feeds.SelectedItems[i]
to give us theName
property of the feed. - In the same breath, we’ll remove the item from the
ltb_feeds
ListBox
as well. - If we failed, we want to tell the user why we failed, and continue on through the loop
We’re done with this part now
Alright Guys and Girls, thanks for reading. Stay tuned for the next one!