Part II of II :Traffic Alert App - Take the pain out of driving

Now for the second part. Incase you have missed the first part you can read it here

So the first part in Window1.xaml.cs is to get the content on loading the Window.

 

 // To use Loaded event put Loaded="WindowLoaded" attribute in root element of .xaml file.private void WindowLoaded(object sender, RoutedEventArgs e) {getRSSfeed();TimerClock = TimerClock = new System.Windows.Threading.DispatcherTimer();TimerClock.Interval = new TimeSpan(1,0,0);TimerClock.IsEnabled = true;TimerClock.Tick += new EventHandler(TimerClock_Tick);} void TimerClock_Tick(object sender, EventArgs e){tv.Items.Clear();getRSSfeed();}

As is evident we get the info from a rss feed. The other notable point here is that we use Timers -
Now what this does is that it updates the content every hour. Maybe you would want to make the time interval shorter.  :)

  private void getRSSfeed(){   string url = "https://maps.yahoo.com/traffic.rss?csz=98052&mag=5&minsev=1";   if (_userZipCodeString != "")   {      url = _userZipCodeString;   }   WebRequest req = WebRequest.Create(url);   WebResponse res = req.GetResponse();   Stream rsstream = res.GetResponseStream();   System.Xml.XmlDataDocument rssdoc = new System.Xml.XmlDataDocument();   rssdoc.Load(rsstream);   System.Xml.XmlNodeList rssitems = rssdoc.SelectNodes("rss/channel/item");   string title = "";   string description = "";   string link = "";   string category = "";   string severity = "";   for (int i = 0; i < rssitems.Count; i++)   {      System.Xml.XmlNode rssdetail;      rssdetail = rssitems.Item(i).SelectSingleNode("title");      if (rssdetail != null)      {         title = rssdetail.InnerText;         rssdetail = rssitems.Item(i).SelectSingleNode("description");         description = rssdetail.InnerText;         rssdetail = rssitems.Item(i).SelectSingleNode("link");         link = (rssdetail != null) ? rssdetail.InnerText : "";         rssdetail = rssitems.Item(i).SelectSingleNode("category");         category = (rssdetail != null) ?rssdetail.InnerText:"";         rssdetail = rssitems.Item(i).SelectSingleNode("severity");         severity = (rssdetail != null) ?rssdetail.InnerText:"";         PopulateList(title, description, link, category, severity);      }      else      {         title = "";      }   }   try   {      rssitems = rssdoc.SelectNodes("rss/channel");      System.Xml.XmlNode rssdetail1;      rssdetail1 = rssitems.Item(0).SelectSingleNode("title");      title = rssdetail1.InnerText;      int index = title.IndexOf("--");      string str = (index > -1)?(title.Substring(index +3)):"Wrong Zip Code";      Location.Content = str;   }   catch (Exception)   {   }}

So you think you saw this code ;). You guessed right . its the same old RSS code. Ok - so we use a rss feed from yahoo maps
and its initially set to redmond since I am located here :). The points to keep in mind regarding the link are:  
csz="ZipCode" minserv=1-4 is the severity levels that you would like to see, 1 being the least severe.

So we have the content . The next thing to do is to populate the TreeView.

 private void PopulateList(string title, string description, string link, string category, string severity){title = title.Replace("\n", "");int index = title.IndexOf(", On ", 0);string str = (index > -1)? title.Substring(index + 5):"NA";string[] charr ={ " At " };string[] strarray = str.Split(charr, StringSplitOptions.None);str = strarray[0];int count = tv.Items.Count;bool found = false;int counter = 0;while ((found == false) && (counter < count)){if (((TreeViewItem)(tv.Items[counter])).Header.ToString() == str){found = true;DockPanel dp = new DockPanel();CreateDockPanelForTreeViewItem(title, description, severity, dp, category, link);((TreeViewItem)(tv.Items[counter])).Items.Add(dp);}counter++;}if (found == false){TreeViewItem tvItem = new TreeViewItem();tvItem.Header = str;tvItem.Foreground = Brushes.White;DockPanel dp = new DockPanel();CreateDockPanelForTreeViewItem(title, description, severity, dp, category, link);tvItem.Items.Add(dp);tvItem.Expanded += new RoutedEventHandler(tvItem_Expanded);tvItem.Collapsed += new RoutedEventHandler(tvItem_Collapsed);tvItem.LostFocus += new RoutedEventHandler(tvItem_LostFocus);tv.Items.Add(tvItem);}return ;}

In the above code we parse the string to get the Highway Name and then look into the tree if this TreeItem is already created.
If it is present, then we add the item to the treeviewItem , else we create a new TreeviewItem.
Each TreeViewItem in turn has a dockPanel which contains a TextBlock having the main info.
Additional info is added in a customized ToolTip as shown below.

 

  private void CreateDockPanelForTreeViewItem(string title, string description, string severity, DockPanel dp, string category, string link){ Label tb = CreateNewLabel(title, severity);dp.Children.Add(GetImage(severity, category, tb.FontSize)); ToolTip tp = new ToolTip();tp.Background = Brushes.Wheat;tp.Padding = new Thickness(4);tp.BorderBrush = Brushes.Gray;  StackPanel sp = new StackPanel(); sp.Width = 250; description = description.Replace("\n", "");string[] strArr = new string[ 6 ];string[] splitArr = {"From Milepost", "Severity:", "Started: ", "Estimated End: ", "Last Updated: "};string _description = description;for (int i = splitArr.Length -1; i >= 0; i--){string[] temp1 = { splitArr[ i ] };string[] strArr1 = _description.Split(temp1, StringSplitOptions.None);int index = _description.IndexOf(splitArr[ i ]);_description = (index > -1) ?_description.Substring(0, index):_description;strArr[i+1] = (strArr1.Length > 1) ? strArr1[1] : null;if (strArr1.Length > 1){strArr[0] = strArr1[0];} }TextBlock top = new TextBlock();top.TextWrapping = TextWrapping.Wrap;top.Background = Brushes.LightSteelBlue;top.FontSize = top.FontSize - 2;top.Text = title; TextBlock bottom = new TextBlock();bottom.TextWrapping = TextWrapping.Wrap;bottom.FontSize = bottom.FontSize - 3;bottom.Background = Brushes.Wheat;bottom.Inlines.Add(strArr[0] + "\r\n");for (int i = 1; i < strArr.Length; i++){if (strArr[ i ] != null){bottom.Inlines.Add(new Bold(new Run(splitArr[i-1])));bottom.Inlines.Add(strArr[ i ] + "\r\n");}} sp.Children.Add(top);sp.Children.Add(bottom);tp.Content = sp;tb.ToolTip = tp; ToolTipService.SetInitialShowDelay(tb, 600);ToolTipService.SetBetweenShowDelay(tb, 600);ToolTipService.SetShowDuration(tb, 30000);((ToolTip)(tb.ToolTip)).Opened += new RoutedEventHandler(Window1_Opened);dp.Children.Add(tb);dp.Background = Brushes.Green;dp.Tag = link;dp.GotFocus += new RoutedEventHandler(dp_GotFocus);dp.LostFocus += new RoutedEventHandler(dp_LostFocus);dp.Focusable = true;}

we use the ToolTip service to set the amount of time the ToolTip stays on the screen. By default, its around 10 secs and this makes reading information difficult. So to solve this issue use the ToolTip Service class.Much of the above code tries to make the tooltip look nice. Otherwise what you would have is a long tooltip which is not good usability. So we customize it to make it more readable and useful.

A useful feature is to actualy open up the map incase you want to see the map. This is done simply by opening the link in a Navigation Window

 static void label_MouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e){try{Label label = sender as Label;DockPanel dp = label.Parent as DockPanel;Uri url = new Uri(dp.Tag.ToString());NavigationWindow nw = new NavigationWindow();nw.Source = url;nw.Show();}catch (Exception){}}

Incase the link is in an incorrect format then the link will not open and no exception is thrown. Thats the reason you see the blank exception. ;)

The above functions form the main part of the code. We have lots of handlers and some helpers like setting background color and such. The complete project can be downloaded here

Have fun :)

trafic.zip