In a previous post across on the Professional Visual Studio blog I walked through getting your first Mesh-enabled Silverlight application up and running. Today I was putting together a bit more of a sample on working with Mesh and got thinking – wouldn’t it be cool if I could use Mesh to replicate out assemblies that could be used to extend an existing application. The scenario would be that someone creates a mesh application with specific extension points. Anyone who belongs to that mesh could then build extensions and make them available to other members on the mesh.
I’ll assume that you have read through my previous post, so I’m not going to cover off building your first Silverlight mesh-enabled application.
The next thing to do was to create a simple Silverlight control library and to use a traditional windows forms application to load this assembly into the mesh corresponding to the application I built. Rather than build out any specific extension points I’m just going to use the Silverlight User Control as a pseudo-interface (clearly it’s not an interface but the fact that I know my type will be a User Control will mean that I can cast it and display it in my Silverlight app – poor man’s application extensibility). There is nothing special about the control library; just use the project template in the new project dialog, give it a name, create a new user control (eg MyUserControl) and build the control library.
The Windows Forms application is where it becomes a little more interesting. In order to work with Mesh you need to start by connecting to the Live Operating Environment, LOE.
private static string meshCloudUrl = @"https://user-ctp.windows.net";
private static void ConnectCloud(string Username, string Password)
{//Get the URI from the input
Uri meshCloudUri = new Uri(meshCloudUrl );System.Net.NetworkCredential credentials = new System.Net.NetworkCredential(Username, Password);
// Use "LiveItemAccessOptions" to pre-fetch the resources
Microsoft.LiveFX.Client.LiveItemAccessOptions accessOptions = new Microsoft.LiveFX.Client.LiveItemAccessOptions(true);ConnectedLiveOperatingEnvironment = new LiveOperatingEnvironment();
ConnectedLiveOperatingEnvironment.Connect(credentials);
}
Once you have connected to LOE you can iterate through resource and make use of the Live Services api. The assembly we created earlier needs to be added to a DataFeed. In this case we will iterate through the existing feeds for the MeshObject corresponding to our Silverlight mesh-enabled application, called MyFirstSilverlightApp. The assembly will be added to a feed called BitsAndPieces. If this DataFeed doesn’t exist, it will be created.
if (ConnectedLiveOperatingEnvironment.IsRunning())
{
MeshObject status = null;
foreach (MeshObject mo in ConnectedLiveOperatingEnvironment.Mesh.MeshObjects.Entries)
{
if (string.Equals(mo.Resource.Title, "MyFirstSilverlightApp"))
{
status = mo;
break;
}
}if (status != null)
{
foreach (DataFeed df in status.DataFeeds.Entries)
{
if (df.Resource.Title == "BitsAndPieces")
{
objectFeed = df;
break;
}
}if (objectFeed==null)
{
objectFeed = new DataFeed("BitsAndPieces");
status.DataFeeds.Add(ref objectFeed);
}
}
}
Now that we have a reference to the DataFeed we just need to add the assembly as a stream.
OpenFileDialog dlg = new OpenFileDialog();
if (dlg.ShowDialog() == DialogResult.OK)
{
FileStream fs = new FileStream(dlg.FileName,FileMode.Open);
objectFeed.DataEntries.Add(fs, "Extension");
}
Running this will add the Silverlight control assembly to the Mesh, which will subsequently be synchronized to those devices on the mesh. This will make it available whereever the MyFirstSilverlightApp is run.
Making use of this extension is essentially the same process but in reverse. The Silverlight application needs to access the mesh environment, read the appropriate feed, load the assembly, create an instance of the type and then display it. To begin with, when the application is loaded you need to get references to the Mesh Service.
private static MeshApplicationService MeshService;
private static LiveOperatingEnvironment MeshEnvironment;public Page()
{
InitializeComponent();MeshService = Application.Current.GetMeshApplicationService();
// once mesh contents are loaded we get callback
MeshService.LoadCompleted += new EventHandler(meshAppLoaded);
MeshService.Load();MeshEnvironment = MeshService.LiveOperatingEnvironment;
}void meshAppLoaded(object sender, EventArgs e)
{
….
}
When the mesh services has completed loading the LoadCompleted event will be raised. In the event handler for this event is a good place for us to find our BitsAndPieces data feed so that we can access our assembly.
foreach (DataFeed df in MeshService.DataFeeds.Entries)
{
if (df.Resource.Title == "BitsAndPieces")
{
foreach (DataEntry de in df.DataEntries.Entries)
{
if (de.Resource.Title == "Extension")
{
System.IO.MemoryStream mstream = new System.IO.MemoryStream();
de.ReadMediaResource(mstream);
AssemblyPart part = new AssemblyPart();
System.Reflection.Assembly asmb =part.Load(mstream);
UserControl uc = Activator.CreateInstance(asmb.GetType("UserControlLibrary.MyUserControl")) as UserControl;
if (uc != null)
{
this.Content = uc;
}
}
}
}
}
In this example all the assembly names and class types are hardcoded. This doesn’t need to be the case and you could iterate through classes in an assembly or use some interfaces or attributes to determine which types can be instantiated for use by the application.