Passing Server information to Silverlight Client

Get Source Here (upgraded to Feb 2009 release of CompositeWPF/Prism)

Uses the CompositeWPF/Prism, Unity and the PresentationModel

Many of us ASP.NET Developers are accustomed to querying our HttpContext.Current object for information about our environment, i.e., we can use it's Server.MapPath("~"); to find our current folder path.

The problem with Silverlight is that it isn't running on the Server - it is running on the client (users computer); it doesn't have access to the HttpContext object.   This can result in having to hardcode information in a configuration file.

We can communicate with the server via WCF services, but it comes with it's own burden of configuration files (created by "Add Web Service") which can become unmanagable quickly in an enterprise level application with 20+ modules.   In recent communications with the Silverlight WCF Services Team I was notified that this is an issue they hope to resolve in SL3 (within the context of providing a SvcUtil.exe for Silverlight).

There is a way we can easily pass information to our Silverlight modules without having to hit the server again - after all, it was the server that served up the page.

THE KEY POINT IN OUR DEMO IS NO CONFIGURATION FILE IS REQUIRED BY OUR MODULE 

The data flow for our server information follows (once data hits the Unity Container it will be available to all modules):

 

For our demo we are working with two property values

  1. ServerURL (http://...) 
  2. ServerPath (C:\..)

Within the context of the attached project, our website's Default.aspx code behind file will load the InitParameters dynamically as follows:

    public partial class _Default : System.Web.UI.Page

    {

        protected void Page_Load(object sender, EventArgs e)

        {

            Xaml1.InitParameters = string.Format(

                "InitParameters="+

                "ServerURL=http://{0};"+

                "ServerPath={1}",

                    Request.Url.Authority,

                    Server.MapPath("~"),

                    ClientQueryString);

        }

    }

 

InitParameters is available via the Silverlight object (defaults to ID=Xaml1 in the default.aspx)

 

<html xmlns="http://www.w3.org/1999/xhtml" style="height:100%;">

<head id="Head1" runat="server">

    <title>UnityContrib.Demo.Silverlight</title>

</head>

<body style="height:100%;margin:0;">

    <form id="form1" runat="server" style="height:100%;">

        <asp:ScriptManager ID="ScriptManager1"
                           runat="server"></asp:ScriptManager>

        <div  style="height:100%;">

            <asp:Silverlight ID="Xaml1"
                 runat="server"
                 Source="~/ClientBin/UnityContrib.Demo.Silverlight.xap"
                
 MinimumVersion="2.0.31005.0" Width="100%" Height="100%" />

        </div>

    </form>

</body>

</html>

The Application_Startup is passed in this value during application startup and assigned to the Bootstrappers.InitParams property:

    public partial class App : Application

    {

        private void Application_Startup(object sender, StartupEventArgs e)

        {

            //this.RootVisual = new Page();

            // Replace default page instantiation with bootstrapper

            Bootstrapper bootstrapper = new Bootstrapper();

            bootstrapper.InitParams = e.InitParams;

            bootstrapper.Run();

        }

When the bootStrapper.Run() command is executed, the BootStrapper's CreateShell() command is executed - it is overridden as shown so that we can load our shell (note that IConfigInfo is registered to use the ConfigInfo class - it processes the InitParameters for us):

protected override DependencyObject CreateShell()

{

    // Singletons

    Container

        .RegisterType<IAppBase, AppBase>

            (new ContainerControlledLifetimeManager())

        .RegisterType<IConfigInfo, ConfigInfo>

            (new ContainerControlledLifetimeManager()) ;

 

    Container.RegisterInstance<IDictionary<string,string>>

        ("InitParameters", InitParams);

 

    Shell shell = this.Container.Resolve<Shell>();

 

#if SILVERLIGHT

    Application.Current.RootVisual = shell;

#else

    shell.Show();

#endif

    return shell;

}

Once an instance of the parameters is registered with the Unity Container it will be available anywhere in the application via Dependency Injection.  We'll show all of the code that was required in the module to generate the following output:

PresentationModel:  (two properties)

public class UploadPresentationModel : ModelBase, IUploadPresentationModel

{

    private string _serverURL;

    public string ServerURL

    {

        get { return _serverURL; }

        set

        {

            _serverURL = value;

            OnPropertyChanged("ServerURL");

        }

    }

 

    private string _serverPath;

    public string ServerPath

    {

        get { return _serverPath; }

        set

        {

            _serverPath = value;

            OnPropertyChanged("ServerPath");

        }

    }

}

VIEW (binds to the ServerURL and ServerPath on it's model):

<UserControl x:Class="Module.Upload.Views.Main.MainView"

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    Width="400" Height="300">

 

    <Grid x:Name="LayoutRoot">

      <Grid.Background>

        <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">

          <GradientStop Color="#FFF8F5F5"/>

          <GradientStop Color="#FF1C6960" Offset="1"/>

        </LinearGradientBrush>

      </Grid.Background>

        <Grid.RowDefinitions>

            <RowDefinition Height="25"/>

            <RowDefinition/>

            <RowDefinition Height="25"/>

        </Grid.RowDefinitions>

        <Grid.ColumnDefinitions>

            <ColumnDefinition />

        </Grid.ColumnDefinitions>

 

        <TextBlock Text="{Binding ServerURL}" Margin="5"/>

        <TextBlock Text="{Binding ServerPath}" Grid.Row="1"/>

    </Grid>

</UserControl>

PRESENTER (uses dependency injection, i.e., IConfigInfo, to obtain configuration info):

public class MainViewPresenter : PresenterBase<IMainView>

{

    IAppBase app;

    IUploadPresentationModel uploadModel;

 

    public MainViewPresenter(

                IConfigInfo configInfo,

                IMainView view,

                IModelBase model,

                IAppBase app)

        // Assign the view and model instances (created by

        // dependency injection) and update base View and Model

        : base(view,model)

    {

        try

        {

            this.app = app;

 

            // Cast the model to this presenter's Model so we

            // can update the applicable properties

            uploadModel = (IUploadPresentationModel) model;

 

            // Update model to reflect current information

            uploadModel.ServerURL = configInfo.WebServerURL;

            uploadModel.ServerPath = configInfo.WebServerPath;

 

        }

        catch (Exception ex)

        {

            throw ex;

        }

    }

 

}

 

Finally - the ConfigInfo class that processes the InitParameters string in its constructor by obtaining an instance of Unity Container via dependency injection (constructor injection).  I will use this so that it can obtain the InitParameters value:

public class ConfigInfo : AppBase, IConfigInfo

{

    private IDictionary<string, string> constructorParam;

    public IDictionary<string, string> InitParam { get; set; }

 

    public ConfigInfo(IUnityContainer container)

    {

        try

        {

            // Instantiate our public Parameter collection

            InitParam = new Dictionary<string, string>();

 

            // Retrieve parameters (saved in Bootstrapper) and place

            // in temporary constructorParam variable

            constructorParam = container

                .Resolve<IDictionary<string, string>>("InitParameters");

 

            // Retrieve from constructorParam the InitParameters value

            // it will be a KeyNameValuePair format.  Split and assign

            // to InitParam public property

            string paraString = constructorParam["InitParameters"];

            string[] paraList = paraString.Split(';');

            foreach (string para in paraList)

            {

                string[] keyValue = para.Split('=');

                InitParam.Add(new

                    KeyValuePair<string, string>(keyValue[0], keyValue[1]));

            }

        }

        catch (Exception ex)

        {

            Error = ex;

        }

    }

 

    /// <summary>

    /// Centralize the retrieval of parameters for error trapping

    /// </summary>

    /// <param name="key"></param>

    /// <returns></returns>

    private string GetParameter(string key)

    {

        try

        {

            return InitParam[key];

        }

        catch (Exception ex)

        {

            Error = ex;

            return string.Empty;

        }

    }

 

    /// <summary>

    /// Web Server URL

    /// </summary>

    public string WebServerURL

    {

        get { return GetParameter("ServerURL"); }

    }

 

    /// <summary>

    /// Web Server Path

    /// </summary>

    public string WebServerPath

    {

        get { return GetParameter("ServerPath"); }

    }

}


Tags: , , ,
Categories: CompositeWPF | Silverlight


Actions: E-mail | Permalink |  Grammar/Typo/Better way? Please let me know