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

Javascript debugging doesn't work in Visual Studio 2008 - or does it?

We need to have Crystal Reports support in Silverlight - an adventure I begin this evening...   I started with the HTML BROWSER INTEGRATION tutorial and plan to have a Silverlight event execute a javascript method to open a new window (providing the necessary parameters) for launching our Crystal Report.

The head-banging session started rather early in the game for less than obvious reasons - I couldn't get a breakpoint to work in my Javascript code - they were being ignored :O

Googling "Visual Studio 2008 Javascript" revealed some pretty cool blogs and webcast suggesting it is a plug'n play so obviously it was something on my machine???  No, so there seems to be a bug...

In all my configuration changes (Debug options as well as project) I didn't think to turn off Silverlight (arrow below) because I need to debug my Silverlight application into the Javascript call HOWEVER!  When I turned this off (unchecked it) I was now successfully debugging my Javascript code as advertised.

 

Seems like an XOR situation; I can either have Silverlight or Javascript debugging but not both....   

I submitted a bug report to Microsoft on the issue so if they are consistent I should have a response within a relatively short period of time letting me know if this is a BUG or FEATURE ;)

 


Tags: , ,
Categories: Silverlight


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

WCF / Silverlight - An error occurred ... attempting to access a service in a cross domain way

When creating a WCF Service Application for your Silverlight application you will encounter a "cross domain" error with default configurations.  

Below I show the default Service application code (only class name changed) and made the necessary Silverlight WCF configuration change for basicHttpBinding as shown by the arrow. 

I then created a Web Service reference for both my Silverlight application and Test application.  Note that Silverlight uses Async calls while the Test project uses Sync call to obtain data.

When I run the Silverlight application I get the following error:

I added the following clientaccesspolicy.xml file per THIS LINK (you can copy code from it) to my WCF Service and both my Silverlight and Unit test run without issue.

 

WCFService.zip (709.94 kb)  <= SOURCE CODE


Tags: ,
Categories: Silverlight


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

Silverlight / CompositeWPF / Prism regions

Suggested reading: Martin Fowler on the PresentationModel 

Source Code available  http://www.CodePlex.com/SDMS  

CompositeWPF/Prism regions permit you to easily design a system that clearly has a Separation of Concerns.  In the figure below you see the XAML code that defines the regions for the Main Employee Manager screen:

This is particularly useful for tabs because you don't have to kludge up your TabControl with alot of controls - above you see all the XAML code required for the Information, In Work and Assigned tabs.

Below you'll see where the Module's Initialize method will register the types, resolve each presenter (instantiate it) and then apply the presenter's view to the applicable regions:


Click HERE for Video Clip Demo (Windows 7 users may have to right click then Save Target As)


The code for the Status Bar's Presenter, View and Interface is below.  Note: all of the Employee Module views share a single presentation model.  

Below is the source code for the EmployeeModule\Views\frmStatus\frmStatusPresenter.cs

using EmployeeModule.PresentationModels;

using Microsoft.Practices.Unity;

using Microsoft.Practices.Composite.Events;

using EmployeeModule.Events;

using System.Windows.Controls;

using System;

 

namespace EmployeeModule.Views.frmStatus

{

    public class frmStatusPresenter

    {

        [Dependency]

        public IEventAggregator aggregator { get; set; }

 

        /// <summary>

        /// View reference

        /// </summary>

        public IfrmStatusView View { get; set; }

 

        /// <summary>

        /// Constructor : setup class

        /// </summary>

        /// <param name="container"></param>

        /// <param name="view"></param>

        public frmStatusPresenter(IUnityContainer container,

            IfrmStatusView view, IEventAggregator aggregator)

        {

            this.View = view;

            this.aggregator = aggregator;

 

            // Provide the presentation model to the view so that

            // it can set it's DataContext to it

            view.Model =
                 container.Resolve<EmployeePresentationModel>();

 

            // Subscribe to the StatusBar event

            aggregator.GetEvent<StatusBarEvent>()

                 .Subscribe(StatusBarEventHandler, true);

 

        }

 

        public void StatusBarEventHandler(StatusBarData panel)

        {

            switch (panel.Panel)

            {

                case StatusPanel.Left:

                    View.FindControl<TextBlock>("stsLeft").Text =
                             panel.Message;

                    View.FindControl<TextBlock>("stsRight").Text =
                             string.Format("ID:{2} -- {0} {1}",

                        View.Model.SelectedEmployee.FirstName,

                        View.Model.SelectedEmployee.LastName,

                        View.Model.SelectedEmployee.EmployeeID);

                    break;

 

                case StatusPanel.Right:

                    View.FindControl<TextBlock>("stsRight").Text =
                            panel.Message;

                    break;

 

            }

        }

    }

}

 


Tags: , ,
Categories: CompositeWPF | Silverlight


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

Silverlight in Expression Blend - how to get calendar control

In watching the Silverlight training video INTRODUCING BLEND TO SILVERLIGHT DEVELOPERS the big question (unanswered up to this point) is "How do you find the Calendar control?". 

Unlike the Visual Studio 2008 toolbox the following is what you'll see by default in Expression Blend:

If you click Show All you'll see the following:

The key to resolving this was to add a calendar control from Visual Studio 2008 and see how it configured it.  Upon doing this I saw the following:

<UserControl xmlns:basics="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls" 

    x:Class="SDMS.Silverlight.Page"

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

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

    Height="500" Width="500" >

    <Grid x:Name="LayoutRoot" Background="White">

      <basics:Calendar></basics:Calendar>

    </Grid>

</UserControl>


I added a reference to System.Windows.Controls and was actually forced to exit Expression Blend and reenter before the reference was updated.  Up to this point I was accustomed to being prompted by VS or Blend when the other system made an updated. Updated: I found that if you add references from Visual Studio and then rebuild the solution that when you switch back to Expression Blend it will prompt you to update your project.

 

Once I re-entered expression blend and loaded my project I was greeted with the following:

The calendar and many other controls (available in the Visual Studio 2008 toolbox) now appear in Expression Blend.


Tags: ,
Categories: Silverlight


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