Dynamically loading controls in Silverlight
Recently I found myself wanting to specify at ‘start up’ time which Control loaded by default in a Silverlight application. For those of you that dont know, you can pass initParams into the Silverlight application that are available at Application Start.
An example of this could be like.
<div id="silverlightControlHost"> <object data="data:application/x-silverlight-2," type="application/x-silverlight-2" width="100%" height="100%"> <param name="source" value="ClientBin/EMASolutions.CMS.Plugins.Silverlight.xap"/> <param name="onError" value="onSilverlightError" /> <param name="background" value="white" /> <param name="minRuntimeVersion" value="3.0.40818.0" /> <param name="autoUpgrade" value="true" /> <param name="initParams" value="firstControl=EMASolutions.CMS.Plugins.Silverlight.FolderBrowser" /> <a href="http://go.microsoft.com/fwlink/?LinkID=149156&v=3.0.40818.0" style="text-decoration:none"> <img src="http://go.microsoft.com/fwlink/?LinkId=108181" alt="Get Microsoft Silverlight" style="border-style:none"/> </a> </object> <iframe id="_sl_historyFrame" style="visibility:hidden;height:0px;width:0px;border:0px"></iframe> </div>
The initParams take the following format, key=value,key1=value1. Notice in this example we passover the fully qualified type name of the control to load on Application Start. This is a simple example, in reality the type name would be driven from user actions and would probably be a persisted value rather than a ‘magic string’.
Anyway, we can access our ‘firstControl’ value like so.
private void Application_Startup(object sender, StartupEventArgs e) { if (!NetworkInterface.GetIsNetworkAvailable()) return; if (e.InitParams.Any()) { if (e.InitParams["firstControl"] != null) LoadControl( (Control) Activator.CreateInstance( sender.GetType().Assembly.GetType(e.InitParams["firstControl"]))); } else { LoadControl( (Control) Activator.CreateInstance( sender.GetType().Assembly.GetType("EMASolutions.CMS.Plugins.Silverlight.FolderBrowser"))); } RootVisual = Container; }
Notice, if the type was not specified, we can load a default control. Again, this is a simple example and the default control could be a persisted value also.
In this example, I am making use of a variable called Container, defined like so.
public static readonly Master Container = new Master();
Master has the following xaml and the default code behind.
<UserControl x:Class="EMASolutions.CMS.Plugins.Silverlight.Master" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" > <Grid x:Name="LayoutRoot"> <Grid.RowDefinitions> <RowDefinition Height="30" /> <RowDefinition Height="*" /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="*" /> </Grid.ColumnDefinitions> <Grid Name="TopBar" Grid.Column="0" Grid.Row="0"></Grid> <Grid Name="Main" Grid.Column="0" Grid.Row="1"></Grid> </Grid> </UserControl>
All that master does, is give us place holders to load our control into, one called Main and one called TopBar. In the Application_Startup method you will also notice the use of a method called LoadControl. This is what loads controls into our container. It is defined like so.
public static void LoadControl(Control control) { if (Container.Main.Children.Any()) LastControl = (Control)Container.Main.Children.First(); Container.Main.Children.Clear(); Container.TopBar.Children.Add(new TopBar()); Container.Main.Children.Add(control); }
We set a LastControl value if there was one, so that we can navigate backwards and then we clear the current controls and add our new control and a TopBar control.
We can also make use of the LoadControl method in other controls. For example, if we have an Authenticate control that logs in a user, we may wish to load another control if authentication failed. We could do this like so.
if (OnFailedAuthentication != null) { OnFailedAuthentication(sender,e); App.LoadControl(new Unauthorised()); }