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.
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
Connection related code spread around for both (1) MulticastClient (2) MulticastHost
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
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<sectionGroup name="applicationSettings" type="System.Configuration.ApplicationSettingsGroup, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
<section name="MulticastClient.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
</sectionGroup>
</configSections>
<system.serviceModel>
<bindings>
<netEventRelayBinding>
<binding name="default" />
</netEventRelayBinding>
</bindings>
<client>
<!--Contract must match in namespace. Be careful if you have a library-->
<!--XML Chunk 1-->
<endpoint name="RelayEndpoint"
contract="DataContracts.IMulticastContract"
binding="netEventRelayBinding"
bindingConfiguration="default"
address="https://AddressToBeReplacedInCode/" />
</client>
</system.serviceModel>
<!--XML Chunk 2-->
<applicationSettings>
<MulticastClient.Properties.Settings>
<setting name="MulticastClient_gov_weather_www_ndfdXML" serializeAs="String">
<value>https://www.weather.gov/forecasts/xml/SOAP_server/ndfdXMLserver.php</value>
</setting>
</MulticastClient.Properties.Settings>
</applicationSettings>
</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
private void button1_Click(object sender, RoutedEventArgs e)
{
// This is button1_Click for MulticastClient, MainWindow.xaml.cs
//------------------------------------------------------------
// Code chunk 1
//------------------------------------------------------------
this.Title = "About to try to connect...";
MyCredentials mycreds = new MyCredentials();
string session = "Bruno Practice Session";
string serviceNamespace = mycreds.ServiceNamespace;
string issuerName = mycreds.IssuerName; // you need to do this part !
string issuerSecret = mycreds.IssuerSecret; // you got it from the web portal
string chatNickname = "The Bruno";
//------------------------------------------------------------
// Code chunk 2
//------------------------------------------------------------
TransportClientEndpointBehavior relayCredentials = new TransportClientEndpointBehavior();
relayCredentials.CredentialType = TransportClientCredentialType.SharedSecret;
relayCredentials.Credentials.SharedSecret.IssuerName = issuerName;
relayCredentials.Credentials.SharedSecret.IssuerSecret = issuerSecret;
Uri serviceAddress = ServiceBusEnvironment.CreateServiceUri("sb", serviceNamespace,
String.Format(CultureInfo.InvariantCulture, "{0}/MulticastService/", session));
//------------------------------------------------------------
// Code chunk 3
//------------------------------------------------------------
channelFactory = new ChannelFactory<IMulticastChannel>("RelayEndpoint", new EndpointAddress(serviceAddress));
channelFactory.Endpoint.Behaviors.Add(relayCredentials);
channel = channelFactory.CreateChannel();
channel.Open();
//------------------------------------------------------------
// // Code chunk 4
//------------------------------------------------------------
string personImage = "/images/connect.png";
Uri uri = new Uri(personImage, UriKind.RelativeOrAbsolute);
BitmapImage source = new BitmapImage(uri);
//------------------------------------------------------------
// // Code chunk 5
//------------------------------------------------------------
this.image1.Source = source;
this.Title = "It appears we are connected. Ready for next step...";
}
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
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<bindings>
<netEventRelayBinding>
<binding name="default" />
</netEventRelayBinding>
</bindings>
<services>
<!--XML Chunk 1-->
<service name="MulticastHost.MulticastService">
<endpoint name="RelayEndpoint"
contract="DataContracts.IMulticastContract"
binding="netEventRelayBinding"
bindingConfiguration="default"
address="" />
</service>
</services>
</system.serviceModel>
</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.”
- Construct an address we can use to connect with. That’s what
- Code chunk 3
- Display an updated connection icon.
- Code chunk 1
Code Snippet
private void button1_Click(object sender, RoutedEventArgs e)
{
// This is button1_Click for MulticastHost, MainWindow.xaml.cs
this.Title = "Prepping credentials...";
MyCredentials mycreds = new MyCredentials();
string session = "Bruno Practice Session";
string serviceNamespace = mycreds.ServiceNamespace;
string issuerName = mycreds.IssuerName;
string issuerSecret = mycreds.IssuerSecret;
//------------------------------------------------------------
// Code chunk 1
//------------------------------------------------------------
TransportClientEndpointBehavior relayCredentials = new TransportClientEndpointBehavior();
relayCredentials.CredentialType = TransportClientCredentialType.SharedSecret;
relayCredentials.Credentials.SharedSecret.IssuerName = issuerName;
relayCredentials.Credentials.SharedSecret.IssuerSecret = issuerSecret;
//------------------------------------------------------------
// Code chunk 2
//------------------------------------------------------------
Uri serviceAddress = ServiceBusEnvironment.CreateServiceUri("sb", serviceNamespace,
String.Format(CultureInfo.InvariantCulture, "{0}/MulticastService/", session));
ServiceHost host = new ServiceHost(typeof(MulticastService), serviceAddress);
host.Description.Endpoints[0].Behaviors.Add(relayCredentials);
this.Title = "About to connect....";
host.Open();
//------------------------------------------------------------
// Code chunk 3
//------------------------------------------------------------
string personImage = "/images/connect.png";
Uri uri = new Uri(personImage, UriKind.RelativeOrAbsolute);
BitmapImage source = new BitmapImage(uri);
this.image1.Source = source;
this.Title = "Connected !";
}
- 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
namespace MulticastHost
{
using System;
using System.ServiceModel;
using DataContracts;
using System.Windows;
using System.Windows.Media;
using System.Xml;
using System.Windows.Media.Imaging;
using System.Drawing;
using System.Net;
using System.IO;
using System.Windows.Controls;
using System.Windows.Documents;
[ServiceBehavior(Name = "MulticastService", Namespace = "https://samples.microsoft.com/ServiceModel/Relay/")]
class MulticastService : IMulticastContract
{
FlowDocumentScrollViewer tf1 = null;
Table table1 = null, table2 = null;
void IMulticastContract.ReportWeather(WeatherInfo weather_info)
{
//--------------------------------------------------------------------------------------------
// Build a poup window
//--------------------------------------------------------------------------------------------
Window mainWindow = new System.Windows.Window();
mainWindow.MaxHeight = 900;
//--------------------------------------------------------------------------------------------
// Use a FlowDocumentScrollViewer(), which is a nice easy to use positioning of controls container
//--------------------------------------------------------------------------------------------
//// Create the parent viewer...
tf1 = new FlowDocumentScrollViewer();
tf1.Document = new FlowDocument();
//--------------------------------------------------------------------------------------------
// We'll populate a couple tables
// ...and add it as a content element of the TextFlow.
//--------------------------------------------------------------------------------------------
table1 = new Table(); // for hi, low, and image array
table2 = new Table(); // for probability of precipitation
//--------------------------------------------------------------------------------------------
tf1.Document.Blocks.Add(table1);
tf1.Document.Blocks.Add(table2);
//--------------------------------------------------------------------------------------------
// Set some global formatting properties for the table.
//--------------------------------------------------------------------------------------------
table1.CellSpacing = 10;
table1.Background = System.Windows.Media.Brushes.White;
//--------------------------------------------------------------------------------------------
// Create 6 columns and add them to the table's Columns collection.
//--------------------------------------------------------------------------------------------
int numberOfColumns = 6;
for (int x = 0; x < numberOfColumns; x++)
table1.Columns.Add(new TableColumn());
//--------------------------------------------------------------------------------------------
// Set alternating background colors for the middle colums.
//--------------------------------------------------------------------------------------------
table1.Columns[1].Background =
table1.Columns[3].Background =
System.Windows.Media.Brushes.LightSteelBlue;
table1.Columns[2].Background =
table1.Columns[4].Background =
System.Windows.Media.Brushes.Beige;
//--------------------------------------------------------------------------------------------
// Create and add an empty TableRowGroup to hold the table's Rows.
//--------------------------------------------------------------------------------------------
table1.RowGroups.Add(new TableRowGroup());
//--------------------------------------------------------------------------------------------
// Add the first (title) row.
//--------------------------------------------------------------------------------------------
table1.RowGroups[0].Rows.Add(new TableRow());
//--------------------------------------------------------------------------------------------
// Alias the current working row for easy reference.
//--------------------------------------------------------------------------------------------
TableRow currentRow = table1.RowGroups[0].Rows[0];
//--------------------------------------------------------------------------------------------
// Global formatting for the title row.
//--------------------------------------------------------------------------------------------
currentRow.Background = System.Windows.Media.Brushes.Silver;
currentRow.FontSize = 40;
currentRow.FontWeight = System.Windows.FontWeights.Bold;
//--------------------------------------------------------------------------------------------
// Add the header row with content. In this case, showing zip code.
//--------------------------------------------------------------------------------------------
currentRow.Cells.Add(new TableCell(new Paragraph(new Run("Weather Report for " + weather_info.ZipCode))));
//--------------------------------------------------------------------------------------------
// and set the row to span all 6 columns. We need a column for each day of weather.
//--------------------------------------------------------------------------------------------
currentRow.Cells[0].ColumnSpan = 6; // just like html, a typical column span
//--------------------------------------------------------------------------------------------
// Add the second (header) row.
//--------------------------------------------------------------------------------------------
table1.RowGroups[0].Rows.Add(new TableRow());
currentRow = table1.RowGroups[0].Rows[1];
//--------------------------------------------------------------------------------------------
// Global formatting for the header row. Big and bold.
//--------------------------------------------------------------------------------------------
currentRow.FontSize = 18;
currentRow.FontWeight = FontWeights.Bold;
//--------------------------------------------------------------------------------------------
// Add cells with content to the second row. Add (1) details (2) Today + next 5 or 6 days
//--------------------------------------------------------------------------------------------
currentRow.Cells.Add(new TableCell(new Paragraph(new Run("Details"))));
currentRow.Cells.Add(new TableCell(new Paragraph(new Run("Today"))));
System.DateTime today = System.DateTime.Now;
for (int i = 1; i < 5; i++)
{
currentRow.Cells.Add(new TableCell(new Paragraph(new Run(string.Format("{0:dddd}", today.AddDays(i))))));
}
//--------------------------------------------------------------------------------------------
// Start a new row for high temperatures
//--------------------------------------------------------------------------------------------
table1.RowGroups[0].Rows.Add(new TableRow());
currentRow = table1.RowGroups[0].Rows[2];
//--------------------------------------------------------------------------------------------
// Global formatting for the row.
//--------------------------------------------------------------------------------------------
currentRow.FontSize = 12;
currentRow.FontWeight = FontWeights.Normal;
//--------------------------------------------------------------------------------------------
// SECTION: HIGH TEMPERATURE
// Add cells with content to the third row. Fill in the high temps.
//--------------------------------------------------------------------------------------------
currentRow.Cells.Add(new TableCell(new Paragraph(new Run("Hi"))));
for (int i = 0; i < weather_info.weather_data.highTempF.Length; i++)
{
currentRow.Cells.Add(new TableCell(new Paragraph(new Run(weather_info.weather_data.highTempF[i]))));
}
//--------------------------------------------------------------------------------------------
// Bold the first cell.
//--------------------------------------------------------------------------------------------
currentRow.Cells[0].FontWeight = FontWeights.Normal;
//--------------------------------------------------------------------------------------------
// Add the fourth row.
//--------------------------------------------------------------------------------------------
table1.RowGroups[0].Rows.Add(new TableRow());
currentRow = table1.RowGroups[0].Rows[3];
//--------------------------------------------------------------------------------------------
// Global formatting for the row. It will hold low temperatures.
//--------------------------------------------------------------------------------------------
currentRow.FontSize = 12;
currentRow.FontWeight = FontWeights.Normal;
//--------------------------------------------------------------------------------------------
// Fill in low temperatures
//--------------------------------------------------------------------------------------------
currentRow.Cells.Add(new TableCell(new Paragraph(new Run("Low"))));
currentRow.FontWeight = FontWeights.Normal;
for (int i = 0; i < weather_info.weather_data.lowTempF.Length; i++)
{
currentRow.Cells.Add(new TableCell(new Paragraph(new Run(weather_info.weather_data.lowTempF[i]))));
}
//--------------------------------------------------------------------------------------------
// Add the fifth row.
//--------------------------------------------------------------------------------------------
table1.RowGroups[0].Rows.Add(new TableRow());
currentRow = table1.RowGroups[0].Rows[4];
// Global formatting for the row.
currentRow.FontSize = 12;
currentRow.FontWeight = FontWeights.Normal;
//--------------------------------------------------------------------------------------------
// Store an image in the table cell. The image will depict the weather.
//--------------------------------------------------------------------------------------------
currentRow.Cells.Add(new TableCell(new Paragraph(new Run("Image"))));
// Calulate height
System.Diagnostics.Debug.Assert(weather_info.weather_data.cloudIconURL.Length > 0);
Bitmap sample_bitmap = LoadPicture(weather_info.weather_data.cloudIconURL[0]);
//--------------------------------------------------------------------------------------------
// Loop through all the cloud icon urls
//--------------------------------------------------------------------------------------------
for (int i = 0; i < weather_info.weather_data.cloudIconURL.Length; i++)
{
//--------------------------------------------------------------------------------------------
// Create a BitmapImage object to become an ImageBrush to become a paragraph in a table cell
//--------------------------------------------------------------------------------------------
BitmapImage bmp0 = new BitmapImage();
bmp0.BeginInit();
bmp0.UriSource = new Uri(weather_info.weather_data.cloudIconURL[i]);
bmp0.EndInit();
Paragraph oParagraph0 = new Paragraph();
//--------------------------------------------------------------------------------------------
// Merge the BitmapImage into the ImageBrush object
//--------------------------------------------------------------------------------------------
ImageBrush ib = new ImageBrush(bmp0);
ib.Stretch = Stretch.None;
ib.AlignmentX = AlignmentX.Left;
//--------------------------------------------------------------------------------------------
// Put the ImageBrush in the paragraph which goes to the table cell
//--------------------------------------------------------------------------------------------
oParagraph0.Background = ib;
//--------------------------------------------------------------------------------------------
// The table cell gets added to the row. The current row will get a cell for
// each day of the week
//--------------------------------------------------------------------------------------------
TableCell c = new TableCell(oParagraph0);
c.LineHeight = ib.ImageSource.Height;
c.LineHeight = sample_bitmap.PhysicalDimension.Height;
currentRow.Cells.Add(c);
}
//--------------------------------------------------------------------------------------------
// Make a normal font row for "More to come..." Give it a Light Gray background
//--------------------------------------------------------------------------------------------
currentRow.Cells[0].FontWeight = FontWeights.Normal;
table1.RowGroups[0].Rows.Add(new TableRow());
currentRow = table1.RowGroups[0].Rows[5];
//--------------------------------------------------------------------------------------------
// Light gray background
//--------------------------------------------------------------------------------------------
currentRow.Background = System.Windows.Media.Brushes.LightGray;
currentRow.FontSize = 18;
currentRow.FontWeight = System.Windows.FontWeights.Normal;
//--------------------------------------------------------------------------------------------
// Just say, "More to come..."
//--------------------------------------------------------------------------------------------
currentRow.Cells.Add(new TableCell(new Paragraph(new Run("More to come..."))));
//--------------------------------------------------------------------------------------------
// and set the row to span all 6 columns.
//--------------------------------------------------------------------------------------------
currentRow.Cells[0].ColumnSpan = 6;
//--------------------------------------------------------------------------------------------
// Get ready to put the satellite map of the weather.
// Add 1 column and make it blue, for the background
//--------------------------------------------------------------------------------------------
table2.CellSpacing = 10;
table2.Background = System.Windows.Media.Brushes.Aqua;
numberOfColumns = 1;
for (int x = 0; x < numberOfColumns; x++)
table2.Columns.Add(new TableColumn());
table2.Columns[0].Background = System.Windows.Media.Brushes.LightSteelBlue;
//--------------------------------------------------------------------------------------------
// Create and add an empty TableRowGroup to hold the table's Rows.
//--------------------------------------------------------------------------------------------
table2.RowGroups.Add(new TableRowGroup());
//--------------------------------------------------------------------------------------------
// Add a new a new row to hold, "Hourly Precipitation Predictions"
//--------------------------------------------------------------------------------------------
table2.RowGroups[0].Rows.Add(new TableRow());
//--------------------------------------------------------------------------------------------
// Alias the current working row for easy reference.
//--------------------------------------------------------------------------------------------
currentRow = table2.RowGroups[0].Rows[0];
//--------------------------------------------------------------------------------------------
// Make a silver row (the column is light steel blue)
//--------------------------------------------------------------------------------------------
currentRow.Background = System.Windows.Media.Brushes.Silver;
currentRow.FontSize = 40;
currentRow.FontWeight = System.Windows.FontWeights.Bold;
//--------------------------------------------------------------------------------------------
// Write the text, "Hourly Precipitation Predictions"
//--------------------------------------------------------------------------------------------
currentRow.Cells.Add(new TableCell(new Paragraph(new Run("Hourly Precipitation Predictions"))));
//--------------------------------------------------------------------------------------------
// Write out hour probability of precipitation (ie, "10:00pm - 75%")
//--------------------------------------------------------------------------------------------
int count = weather_info.weather_data.prob_precip.Length;
for (int i = 0; i < count; i++)
{
//--------------------------------------------------------------------------------------------
// One of 8 rows, one for each hour
//--------------------------------------------------------------------------------------------
table2.RowGroups[0].Rows.Add(new TableRow());
currentRow = table2.RowGroups[0].Rows[i + 1];
currentRow.FontSize = 14;
currentRow.FontWeight = FontWeights.Normal;
string the_row = DateTime.Now.AddHours(i).ToShortTimeString() + " - " + weather_info.weather_data.prob_precip[i] + " %";
//--------------------------------------------------------------------------------------------
// Each cell is in a paragraph, making it effectivily like a new row
//--------------------------------------------------------------------------------------------
currentRow.Cells.Add(new TableCell(new Paragraph(new Run(the_row))));
}
//--------------------------------------------------------------------------------------------
// Make a row to hold the satellite image
//--------------------------------------------------------------------------------------------
table2.RowGroups[0].Rows.Add(new TableRow());
currentRow = table2.RowGroups[0].Rows[count + 1];
currentRow.FontSize = 14;
currentRow.FontWeight = FontWeights.Normal;
//--------------------------------------------------------------------------------------------
// This url holds the latest photo that we want to download
//--------------------------------------------------------------------------------------------
Bitmap bmpSat = LoadPicture(@"https://www.ssd.noaa.gov/goes/west/nepac/avn-l.jpg");
MemoryStream ms = new MemoryStream();
bmpSat.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
BitmapImage bImg = new BitmapImage();
bImg.BeginInit();
bImg.StreamSource = new MemoryStream(ms.ToArray());
bImg.EndInit();
//--------------------------------------------------------------------------------------------
// The paragraph will hold the satellite image.
//--------------------------------------------------------------------------------------------
Paragraph oParagraphSat = new Paragraph();
ImageBrush ibSat = new ImageBrush();
ibSat.ImageSource = bImg;
ibSat.Stretch = Stretch.None;
ibSat.AlignmentX = AlignmentX.Left;
oParagraphSat.Background = ibSat;
//--------------------------------------------------------------------------------------------
// Paragraph added to the table cell
//--------------------------------------------------------------------------------------------
TableCell mycell = new TableCell(oParagraphSat);
mycell.LineHeight = bmpSat.PhysicalDimension.Height;
currentRow.Cells.Add(mycell);
//--------------------------------------------------------------------------------------------
// Give the new will a title and show it
//--------------------------------------------------------------------------------------------
mainWindow.Title = "Weather Report";
mainWindow.Content = tf1;
mainWindow.Show();
}
//--------------------------------------------------------------------------------------------
// Method that returns a bitmap object from a url
//--------------------------------------------------------------------------------------------
private Bitmap LoadPicture(string url)
{
//--------------------------------------------------------------------------------------------
// Use the web request model and just stream the bytes into place
//--------------------------------------------------------------------------------------------
HttpWebRequest wreq;
HttpWebResponse wresp;
Stream mystream;
Bitmap bmp;
bmp = null;
mystream = null;
wresp = null;
//--------------------------------------------------------------------------------------------
// Stream the binary results of the url into our Bitmap object
//--------------------------------------------------------------------------------------------
try
{
wreq = (HttpWebRequest)WebRequest.Create(url);
wreq.AllowWriteStreamBuffering = true;
wresp = (HttpWebResponse)wreq.GetResponse();
if ((mystream = wresp.GetResponseStream()) != null)
bmp = new Bitmap(mystream);
}
finally
{
if (mystream != null)
mystream.Close();
if (wresp != null)
wresp.Close();
}
return (bmp);
}
}
}