Multicasting Messages with AppFabric (Service Bus & Access Control) – Listener Popping Up XAML Windows Based on Events

Make sure to visit Part 1. This is a continuation.

https://blogs.msdn.com/brunoterkaly/archive/2010/01/19/multicasting-messages-the-world-opens-up-spreading-the-message-part-1-of-10.aspx

Configuration Files (for MulticastClient and MulticastHost)

Learning the basics now for each side of the conversation is key. The configuration for Publisher and Subscriber is quite different in both code behind and in markup (xml) in the app.config file.

Lets explore each of the following:

  • App.config MulticastClient
  • Code-behind MulticastClient
  • App.config MulticastHost
  • Code-behind MulticastHost

image

 

App.config MulticastClient

  • XML Chunk 1
    • This section defines the (1) contract (2) binding that client is using. The address part of the connection is added with code, where we construct the uri (see next code snippet (chunk 2) after this one).
    • This is how the client connects to the service bus. Code behind is present and is required for this to work.
  • XML Chunk 2
    • this code was injected by the Visual Studio 2010 tooling when we added a “web reference” to the National Weather Service.
    • It is used to connect to the National Weather Service. Note the PHP file we point to which returns our weather.

Code Snippet

  1. <?xml version="1.0" encoding="utf-8" ?>

  2. <configuration>

  3.   <configSections>

  4.     <sectionGroup name="applicationSettings" type="System.Configuration.ApplicationSettingsGroup, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >

  5.       <section name="MulticastClient.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />

  6.     </sectionGroup>

  7.   </configSections>

  8.   <system.serviceModel>

  9.     <bindings>

  10.       <netEventRelayBinding>

  11.         <binding name="default" />

  12.       </netEventRelayBinding>

  13.     </bindings>

  14.     <client>

  15.       <!--Contract must match in namespace. Be careful if you have a library-->

  16.       <!--XML Chunk 1-->

  17.       <endpoint name="RelayEndpoint"

  18.                 contract="DataContracts.IMulticastContract"

  19.                 binding="netEventRelayBinding"

  20.                 bindingConfiguration="default"

  21.                 address="https://AddressToBeReplacedInCode/" />

  22.     </client>

  23.   </system.serviceModel>

  24.   <!--XML Chunk 2-->

  25.   <applicationSettings>

  26.     <MulticastClient.Properties.Settings>

  27.       <setting name="MulticastClient_gov_weather_www_ndfdXML" serializeAs="String">

  28.         <value>https://www.weather.gov/forecasts/xml/SOAP_server/ndfdXMLserver.php</value>

  29.       </setting>

  30.     </MulticastClient.Properties.Settings>

  31.   </applicationSettings>

  32. </configuration>

 

Code-behind MulticastClient.xaml.cs

Note the following:

  • Code chunk 1
    • Uses the issuer name and secret that you got when you signed up. You need to to go the Azure Web Portal for this.
  • Code chunk 2
    • Preparing our credentials so the client can be authorized to publish to the service bus end point.
    • Construct an address we can use to connect with. That’s what
      • This address points to the cloud.
    • Notice we have “MulticastServce” as part of the address. You can make up your own but make sure both client and host use the same endpoint.
  • Code chunk 3
    • Adds everything up and opens a connection
  • Code chunk 4 & 5
    • Display the connection icon

Code Snippet

  1.    private void button1_Click(object sender, RoutedEventArgs e)

  2.    {

  3.        // This is button1_Click for MulticastClient, MainWindow.xaml.cs

  4.        //------------------------------------------------------------

  5.        // Code chunk 1

  6.        //------------------------------------------------------------

  7.        this.Title = "About to try to connect...";

  8.        MyCredentials mycreds = new MyCredentials();

  9.        string session = "Bruno Practice Session";

  10.        string serviceNamespace = mycreds.ServiceNamespace;

  11.        string issuerName = mycreds.IssuerName;     // you need to do this part !

  12.        string issuerSecret = mycreds.IssuerSecret; // you got it from the web portal

  13.        string chatNickname = "The Bruno";

  14.        //------------------------------------------------------------

  15.        // Code chunk 2

  16.        //------------------------------------------------------------

  17.        TransportClientEndpointBehavior relayCredentials = new TransportClientEndpointBehavior();

  18.        relayCredentials.CredentialType = TransportClientCredentialType.SharedSecret;

  19.        relayCredentials.Credentials.SharedSecret.IssuerName = issuerName;

  20.        relayCredentials.Credentials.SharedSecret.IssuerSecret = issuerSecret;

  21.        Uri serviceAddress = ServiceBusEnvironment.CreateServiceUri("sb", serviceNamespace,

  22.               String.Format(CultureInfo.InvariantCulture, "{0}/MulticastService/", session));

  23.        //------------------------------------------------------------

  24.        // Code chunk 3

  25.        //------------------------------------------------------------

  26.        channelFactory = new ChannelFactory<IMulticastChannel>("RelayEndpoint", new EndpointAddress(serviceAddress));

  27.        channelFactory.Endpoint.Behaviors.Add(relayCredentials);

  28.        channel = channelFactory.CreateChannel();

  29.        channel.Open();

  30.        //------------------------------------------------------------

  31.        // // Code chunk 4

  32.        //------------------------------------------------------------

  33.        string personImage = "/images/connect.png";

  34.        Uri uri = new Uri(personImage, UriKind.RelativeOrAbsolute);

  35.        BitmapImage source = new BitmapImage(uri);

  36.        //------------------------------------------------------------

  37.        // // Code chunk 5

  38.        //------------------------------------------------------------

  39.        this.image1.Source = source;

  40.        this.Title = "It appears we are connected. Ready for next step...";

  41.    }

App.config MulticastHost

XML Chunk 1

  • This section defines the (1) contract (2) binding. The address is added with code, where we construct the uri (see next code snippet after this one).
  • This is how the host connects to the service bus to listen for message from MulticastClient. This is the subscriber. We can have more than one subscriber.

Code Snippet

  1. <?xml version="1.0" encoding="utf-8" ?>

  2. <configuration>

  3.   <system.serviceModel>

  4.     <bindings>

  5.       <netEventRelayBinding>

  6.         <binding name="default" />

  7.       </netEventRelayBinding>

  8.     </bindings>

  9.     

  10.     <services>

  11.       <!--XML Chunk 1-->

  12.       <service name="MulticastHost.MulticastService">

  13.         <endpoint name="RelayEndpoint"

  14.                   contract="DataContracts.IMulticastContract"

  15.                   binding="netEventRelayBinding"

  16.                   bindingConfiguration="default"

  17.                   address="" />

  18.       </service>

  19.     </services>

  20.   </system.serviceModel>

  21. </configuration>

Code-behind MulticastHost

Note the following:

    • Code chunk 1
      • Uses the issuer name and secret that you got when you signed up. You need to to go the Azure Web Portal for this.
      • Preparing our credentials so the client can be authorized to publish to the service bus end point.
    • Code chunk 2
      • Construct an address we can use to connect with. That’s what
        • This address points to the cloud.
      • Notice we have “MulticastServce” as part of the address. You can make up your own but make sure both client and host use the same endpoint.
      • As a listener, we need to instantiate a ServiceHost() object, of type “MulticastService.”
    • Code chunk 3
      • Display an updated connection icon.

Code Snippet

  1. private void button1_Click(object sender, RoutedEventArgs e)

  2.  {

  3.      // This is button1_Click for MulticastHost, MainWindow.xaml.cs

  4.      this.Title = "Prepping credentials...";

  5.      MyCredentials mycreds = new MyCredentials();

  6.      string session = "Bruno Practice Session";

  7.      string serviceNamespace = mycreds.ServiceNamespace;

  8.      string issuerName = mycreds.IssuerName;

  9.      string issuerSecret = mycreds.IssuerSecret;

  10.      //------------------------------------------------------------

  11.      // Code chunk 1

  12.      //------------------------------------------------------------

  13.      TransportClientEndpointBehavior relayCredentials = new TransportClientEndpointBehavior();

  14.      relayCredentials.CredentialType = TransportClientCredentialType.SharedSecret;

  15.      relayCredentials.Credentials.SharedSecret.IssuerName = issuerName;

  16.      relayCredentials.Credentials.SharedSecret.IssuerSecret = issuerSecret;

  17.      //------------------------------------------------------------

  18.      // Code chunk 2

  19.      //------------------------------------------------------------

  20.      Uri serviceAddress = ServiceBusEnvironment.CreateServiceUri("sb", serviceNamespace,

  21.             String.Format(CultureInfo.InvariantCulture, "{0}/MulticastService/", session));

  22.      ServiceHost host = new ServiceHost(typeof(MulticastService), serviceAddress);

  23.      host.Description.Endpoints[0].Behaviors.Add(relayCredentials);

  24.      this.Title = "About to connect....";

  25.      host.Open();

  26.      //------------------------------------------------------------

  27.      // Code chunk 3

  28.      //------------------------------------------------------------

  29.      string personImage = "/images/connect.png";

  30.      Uri uri = new Uri(personImage, UriKind.RelativeOrAbsolute);

  31.      BitmapImage source = new BitmapImage(uri);

  32.      this.image1.Source = source;

  33.      this.Title = "Connected !";

  34.  }

 

 

image

  • Very simply put – the code below, creates the window above. The code below is very simple. IMulticastService implements IMulticastContract.ReportWeather(WeatherInfo weather_info).
  • We have 2 tables
    • Table 1 has 6 rows and 6 columns
    • Table 2 has 3 rows and 1 columns

Code Snippet

  1. namespace MulticastHost

  2. {

  3.     using System;

  4.     using System.ServiceModel;

  5.     using DataContracts;

  6.     using System.Windows;

  7.     using System.Windows.Media;

  8.     using System.Xml;

  9.     using System.Windows.Media.Imaging;

  10.     using System.Drawing;

  11.     using System.Net;

  12.     using System.IO;

  13.     using System.Windows.Controls;

  14.     using System.Windows.Documents;

  15.     [ServiceBehavior(Name = "MulticastService", Namespace = "https://samples.microsoft.com/ServiceModel/Relay/")]

  16.     class MulticastService : IMulticastContract

  17.     {

  18.         FlowDocumentScrollViewer tf1 = null;

  19.         Table table1 = null, table2 = null;

  20.         void IMulticastContract.ReportWeather(WeatherInfo weather_info)

  21.         {

  22.             //--------------------------------------------------------------------------------------------

  23.             // Build a poup window

  24.             //--------------------------------------------------------------------------------------------

  25.             Window mainWindow = new System.Windows.Window();

  26.             mainWindow.MaxHeight = 900;

  27.             //--------------------------------------------------------------------------------------------

  28.             // Use a FlowDocumentScrollViewer(), which is a nice easy to use positioning of controls container

  29.             //--------------------------------------------------------------------------------------------

  30.             //// Create the parent viewer...

  31.             tf1 = new FlowDocumentScrollViewer();

  32.             tf1.Document = new FlowDocument();

  33.             //--------------------------------------------------------------------------------------------

  34.             // We'll populate a couple tables

  35.             // ...and add it as a content element of the TextFlow.

  36.             //--------------------------------------------------------------------------------------------

  37.             table1 = new Table(); // for hi, low, and image array

  38.             table2 = new Table(); // for probability of precipitation

  39.             //--------------------------------------------------------------------------------------------

  40.             tf1.Document.Blocks.Add(table1);

  41.             tf1.Document.Blocks.Add(table2);

  42.             //--------------------------------------------------------------------------------------------

  43.             // Set some global formatting properties for the table.

  44.             //--------------------------------------------------------------------------------------------

  45.             table1.CellSpacing = 10;

  46.             table1.Background = System.Windows.Media.Brushes.White;

  47.             //--------------------------------------------------------------------------------------------

  48.             // Create 6 columns and add them to the table's Columns collection.

  49.             //--------------------------------------------------------------------------------------------

  50.             int numberOfColumns = 6;

  51.             for (int x = 0; x < numberOfColumns; x++)

  52.                 table1.Columns.Add(new TableColumn());

  53.             //--------------------------------------------------------------------------------------------

  54.             // Set alternating background colors for the middle colums.

  55.             //--------------------------------------------------------------------------------------------

  56.             table1.Columns[1].Background =

  57.                 table1.Columns[3].Background =

  58.                 System.Windows.Media.Brushes.LightSteelBlue;

  59.             table1.Columns[2].Background =

  60.                 table1.Columns[4].Background =

  61.                 System.Windows.Media.Brushes.Beige;

  62.             //--------------------------------------------------------------------------------------------

  63.             // Create and add an empty TableRowGroup to hold the table's Rows.

  64.             //--------------------------------------------------------------------------------------------

  65.             table1.RowGroups.Add(new TableRowGroup());

  66.             //--------------------------------------------------------------------------------------------

  67.             // Add the first (title) row.

  68.             //--------------------------------------------------------------------------------------------

  69.             table1.RowGroups[0].Rows.Add(new TableRow());

  70.             //--------------------------------------------------------------------------------------------

  71.             // Alias the current working row for easy reference.

  72.             //--------------------------------------------------------------------------------------------

  73.             TableRow currentRow = table1.RowGroups[0].Rows[0];

  74.             //--------------------------------------------------------------------------------------------

  75.             // Global formatting for the title row.

  76.             //--------------------------------------------------------------------------------------------

  77.             currentRow.Background = System.Windows.Media.Brushes.Silver;

  78.             currentRow.FontSize = 40;

  79.             currentRow.FontWeight = System.Windows.FontWeights.Bold;

  80.             //--------------------------------------------------------------------------------------------

  81.             // Add the header row with content. In this case, showing zip code.

  82.             //--------------------------------------------------------------------------------------------

  83.             currentRow.Cells.Add(new TableCell(new Paragraph(new Run("Weather Report for " + weather_info.ZipCode))));

  84.             //--------------------------------------------------------------------------------------------

  85.             // and set the row to span all 6 columns. We need a column for each day of weather.

  86.             //--------------------------------------------------------------------------------------------

  87.             currentRow.Cells[0].ColumnSpan = 6;  // just like html, a typical column span

  88.             //--------------------------------------------------------------------------------------------

  89.             // Add the second (header) row.

  90.             //--------------------------------------------------------------------------------------------

  91.             table1.RowGroups[0].Rows.Add(new TableRow());

  92.             currentRow = table1.RowGroups[0].Rows[1];

  93.             //--------------------------------------------------------------------------------------------

  94.             // Global formatting for the header row. Big and bold.

  95.             //--------------------------------------------------------------------------------------------

  96.             currentRow.FontSize = 18;

  97.             currentRow.FontWeight = FontWeights.Bold;

  98.             //--------------------------------------------------------------------------------------------

  99.             // Add cells with content to the second row. Add (1) details (2) Today + next 5 or 6 days

  100.             //--------------------------------------------------------------------------------------------

  101.             currentRow.Cells.Add(new TableCell(new Paragraph(new Run("Details"))));

  102.             currentRow.Cells.Add(new TableCell(new Paragraph(new Run("Today"))));

  103.             System.DateTime today = System.DateTime.Now;

  104.             for (int i = 1; i < 5; i++)

  105.             {

  106.                 currentRow.Cells.Add(new TableCell(new Paragraph(new Run(string.Format("{0:dddd}", today.AddDays(i))))));

  107.             }

  108.             //--------------------------------------------------------------------------------------------

  109.             // Start a new row for high temperatures

  110.             //--------------------------------------------------------------------------------------------

  111.             table1.RowGroups[0].Rows.Add(new TableRow());

  112.             currentRow = table1.RowGroups[0].Rows[2];

  113.             //--------------------------------------------------------------------------------------------

  114.             // Global formatting for the row.

  115.             //--------------------------------------------------------------------------------------------

  116.             currentRow.FontSize = 12;

  117.             currentRow.FontWeight = FontWeights.Normal;

  118.             //--------------------------------------------------------------------------------------------

  119.             // SECTION: HIGH TEMPERATURE

  120.             // Add cells with content to the third row. Fill in the high temps.

  121.             //--------------------------------------------------------------------------------------------

  122.             currentRow.Cells.Add(new TableCell(new Paragraph(new Run("Hi"))));

  123.             for (int i = 0; i < weather_info.weather_data.highTempF.Length; i++)

  124.             {

  125.                 currentRow.Cells.Add(new TableCell(new Paragraph(new Run(weather_info.weather_data.highTempF[i]))));

  126.             }

  127.             //--------------------------------------------------------------------------------------------

  128.             // Bold the first cell.

  129.             //--------------------------------------------------------------------------------------------

  130.             currentRow.Cells[0].FontWeight = FontWeights.Normal;

  131.             //--------------------------------------------------------------------------------------------

  132.             // Add the fourth row.

  133.             //--------------------------------------------------------------------------------------------

  134.             table1.RowGroups[0].Rows.Add(new TableRow());

  135.             currentRow = table1.RowGroups[0].Rows[3];

  136.             //--------------------------------------------------------------------------------------------

  137.             // Global formatting for the row. It will hold low temperatures.

  138.             //--------------------------------------------------------------------------------------------

  139.             currentRow.FontSize = 12;

  140.             currentRow.FontWeight = FontWeights.Normal;

  141.             //--------------------------------------------------------------------------------------------

  142.             // Fill in low temperatures

  143.             //--------------------------------------------------------------------------------------------

  144.             currentRow.Cells.Add(new TableCell(new Paragraph(new Run("Low"))));

  145.             currentRow.FontWeight = FontWeights.Normal;

  146.             for (int i = 0; i < weather_info.weather_data.lowTempF.Length; i++)

  147.             {

  148.                 currentRow.Cells.Add(new TableCell(new Paragraph(new Run(weather_info.weather_data.lowTempF[i]))));

  149.             }

  150.             //--------------------------------------------------------------------------------------------

  151.             // Add the fifth row.

  152.             //--------------------------------------------------------------------------------------------

  153.             table1.RowGroups[0].Rows.Add(new TableRow());

  154.             currentRow = table1.RowGroups[0].Rows[4];

  155.             // Global formatting for the row.

  156.             currentRow.FontSize = 12;

  157.             currentRow.FontWeight = FontWeights.Normal;

  158.             //--------------------------------------------------------------------------------------------

  159.             // Store an image in the table cell. The image will depict the weather.

  160.             //--------------------------------------------------------------------------------------------

  161.             currentRow.Cells.Add(new TableCell(new Paragraph(new Run("Image"))));

  162.             // Calulate height

  163.             System.Diagnostics.Debug.Assert(weather_info.weather_data.cloudIconURL.Length > 0);

  164.             Bitmap sample_bitmap = LoadPicture(weather_info.weather_data.cloudIconURL[0]);

  165.             //--------------------------------------------------------------------------------------------

  166.             // Loop through all the cloud icon urls

  167.             //--------------------------------------------------------------------------------------------

  168.             for (int i = 0; i < weather_info.weather_data.cloudIconURL.Length; i++)

  169.             {

  170.                 //--------------------------------------------------------------------------------------------

  171.                 // Create a BitmapImage object to become an ImageBrush to become a paragraph in a table cell

  172.                 //--------------------------------------------------------------------------------------------

  173.                 BitmapImage bmp0 = new BitmapImage();

  174.                 bmp0.BeginInit();

  175.                 bmp0.UriSource = new Uri(weather_info.weather_data.cloudIconURL[i]);

  176.                 bmp0.EndInit();

  177.                 Paragraph oParagraph0 = new Paragraph();

  178.                 //--------------------------------------------------------------------------------------------

  179.                 // Merge the BitmapImage into the ImageBrush object

  180.                 //--------------------------------------------------------------------------------------------

  181.                 ImageBrush ib = new ImageBrush(bmp0);

  182.                 ib.Stretch = Stretch.None;

  183.                 ib.AlignmentX = AlignmentX.Left;

  184.                 //--------------------------------------------------------------------------------------------

  185.                 // Put the ImageBrush in the paragraph which goes to the table cell

  186.                 //--------------------------------------------------------------------------------------------

  187.                 oParagraph0.Background = ib;

  188.                 //--------------------------------------------------------------------------------------------

  189.                 // The table cell gets added to the row. The current row will get a cell for

  190.                 // each day of the week

  191.                 //--------------------------------------------------------------------------------------------

  192.                 TableCell c = new TableCell(oParagraph0);

  193.                 c.LineHeight = ib.ImageSource.Height;

  194.                 c.LineHeight = sample_bitmap.PhysicalDimension.Height;

  195.                 currentRow.Cells.Add(c);

  196.             }

  197.             //--------------------------------------------------------------------------------------------

  198.             // Make a normal font row for "More to come..." Give it a Light Gray background

  199.             //--------------------------------------------------------------------------------------------

  200.             currentRow.Cells[0].FontWeight = FontWeights.Normal;

  201.             table1.RowGroups[0].Rows.Add(new TableRow());

  202.             currentRow = table1.RowGroups[0].Rows[5];

  203.             //--------------------------------------------------------------------------------------------

  204.             // Light gray background

  205.             //--------------------------------------------------------------------------------------------

  206.             currentRow.Background = System.Windows.Media.Brushes.LightGray;

  207.             currentRow.FontSize = 18;

  208.             currentRow.FontWeight = System.Windows.FontWeights.Normal;

  209.             //--------------------------------------------------------------------------------------------

  210.             // Just say, "More to come..."

  211.             //--------------------------------------------------------------------------------------------

  212.             currentRow.Cells.Add(new TableCell(new Paragraph(new Run("More to come..."))));

  213.             //--------------------------------------------------------------------------------------------

  214.             // and set the row to span all 6 columns.

  215.             //--------------------------------------------------------------------------------------------

  216.             currentRow.Cells[0].ColumnSpan = 6;

  217.             //--------------------------------------------------------------------------------------------

  218.             // Get ready to put the satellite map of the weather.

  219.             // Add 1 column and make it blue, for the background

  220.             //--------------------------------------------------------------------------------------------

  221.             table2.CellSpacing = 10;

  222.             table2.Background = System.Windows.Media.Brushes.Aqua;

  223.             numberOfColumns = 1;

  224.             for (int x = 0; x < numberOfColumns; x++)

  225.                 table2.Columns.Add(new TableColumn());

  226.             table2.Columns[0].Background = System.Windows.Media.Brushes.LightSteelBlue;

  227.             //--------------------------------------------------------------------------------------------

  228.             // Create and add an empty TableRowGroup to hold the table's Rows.

  229.             //--------------------------------------------------------------------------------------------

  230.             table2.RowGroups.Add(new TableRowGroup());

  231.             //--------------------------------------------------------------------------------------------

  232.             // Add a new a new row to hold, "Hourly Precipitation Predictions"

  233.             //--------------------------------------------------------------------------------------------

  234.             table2.RowGroups[0].Rows.Add(new TableRow());

  235.             //--------------------------------------------------------------------------------------------

  236.             // Alias the current working row for easy reference.

  237.             //--------------------------------------------------------------------------------------------

  238.             currentRow = table2.RowGroups[0].Rows[0];

  239.             //--------------------------------------------------------------------------------------------

  240.             // Make a silver row (the column is light steel blue)

  241.             //--------------------------------------------------------------------------------------------

  242.             currentRow.Background = System.Windows.Media.Brushes.Silver;

  243.             currentRow.FontSize = 40;

  244.             currentRow.FontWeight = System.Windows.FontWeights.Bold;

  245.             //--------------------------------------------------------------------------------------------

  246.             // Write the text, "Hourly Precipitation Predictions"

  247.             //--------------------------------------------------------------------------------------------

  248.             currentRow.Cells.Add(new TableCell(new Paragraph(new Run("Hourly Precipitation Predictions"))));

  249.             //--------------------------------------------------------------------------------------------

  250.             // Write out hour probability of precipitation (ie, "10:00pm - 75%")

  251.             //--------------------------------------------------------------------------------------------

  252.             int count = weather_info.weather_data.prob_precip.Length;

  253.             for (int i = 0; i < count; i++)

  254.             {

  255.                 //--------------------------------------------------------------------------------------------

  256.                 // One of 8 rows, one for each hour

  257.                 //--------------------------------------------------------------------------------------------

  258.                 table2.RowGroups[0].Rows.Add(new TableRow());

  259.                 currentRow = table2.RowGroups[0].Rows[i + 1];

  260.                 currentRow.FontSize = 14;

  261.                 currentRow.FontWeight = FontWeights.Normal;

  262.                 string the_row = DateTime.Now.AddHours(i).ToShortTimeString() + " - " + weather_info.weather_data.prob_precip[i] + " %";

  263.                 //--------------------------------------------------------------------------------------------

  264.                 // Each cell is in a paragraph, making it effectivily like a new row

  265.                 //--------------------------------------------------------------------------------------------

  266.                 currentRow.Cells.Add(new TableCell(new Paragraph(new Run(the_row))));

  267.             }

  268.             //--------------------------------------------------------------------------------------------

  269.             // Make a row to hold the satellite image

  270.             //--------------------------------------------------------------------------------------------

  271.             table2.RowGroups[0].Rows.Add(new TableRow());

  272.             currentRow = table2.RowGroups[0].Rows[count + 1];

  273.             currentRow.FontSize = 14;

  274.             currentRow.FontWeight = FontWeights.Normal;

  275.             //--------------------------------------------------------------------------------------------

  276.             // This url holds the latest photo that we want to download

  277.             //--------------------------------------------------------------------------------------------

  278.             Bitmap bmpSat = LoadPicture(@"https://www.ssd.noaa.gov/goes/west/nepac/avn-l.jpg");

  279.             MemoryStream ms = new MemoryStream();

  280.             bmpSat.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);

  281.             BitmapImage bImg = new BitmapImage();

  282.             bImg.BeginInit();

  283.             bImg.StreamSource = new MemoryStream(ms.ToArray());

  284.             bImg.EndInit();

  285.             //--------------------------------------------------------------------------------------------

  286.             // The paragraph will hold the satellite image.

  287.             //--------------------------------------------------------------------------------------------

  288.             Paragraph oParagraphSat = new Paragraph();

  289.             ImageBrush ibSat = new ImageBrush();

  290.             ibSat.ImageSource = bImg;

  291.             ibSat.Stretch = Stretch.None;

  292.             ibSat.AlignmentX = AlignmentX.Left;

  293.             oParagraphSat.Background = ibSat;

  294.             //--------------------------------------------------------------------------------------------

  295.             // Paragraph added to the table cell

  296.             //--------------------------------------------------------------------------------------------

  297.             TableCell mycell = new TableCell(oParagraphSat);

  298.             mycell.LineHeight = bmpSat.PhysicalDimension.Height;

  299.             currentRow.Cells.Add(mycell);

  300.             //--------------------------------------------------------------------------------------------

  301.             // Give the new will a title and show it

  302.             //--------------------------------------------------------------------------------------------

  303.             mainWindow.Title = "Weather Report";

  304.             mainWindow.Content = tf1;

  305.             mainWindow.Show();

  306.         }

  307.         //--------------------------------------------------------------------------------------------

  308.         // Method that returns a bitmap object from a url

  309.         //--------------------------------------------------------------------------------------------

  310.         private Bitmap LoadPicture(string url)

  311.         {

  312.             //--------------------------------------------------------------------------------------------

  313.             // Use the web request model and just stream the bytes into place

  314.             //--------------------------------------------------------------------------------------------

  315.             HttpWebRequest wreq;

  316.             HttpWebResponse wresp;

  317.             Stream mystream;

  318.             Bitmap bmp;

  319.             bmp = null;

  320.             mystream = null;

  321.             wresp = null;

  322.             //--------------------------------------------------------------------------------------------

  323.             // Stream the binary results of the url into our Bitmap object

  324.             //--------------------------------------------------------------------------------------------

  325.             try

  326.             {

  327.                 wreq = (HttpWebRequest)WebRequest.Create(url);

  328.                 wreq.AllowWriteStreamBuffering = true;

  329.                 wresp = (HttpWebResponse)wreq.GetResponse();

  330.                 if ((mystream = wresp.GetResponseStream()) != null)

  331.                     bmp = new Bitmap(mystream);

  332.             }

  333.             finally

  334.             {

  335.                 if (mystream != null)

  336.                     mystream.Close();

  337.                 if (wresp != null)

  338.                     wresp.Close();

  339.             }

  340.             return (bmp);

  341.         }

  342.     }

  343. }