JavaScript Breakpoints Not Working In Visual Studio 2008

This can happen, sometimes fixed by re-installing VS or installing VS 2008 SP1. In this case it seems Javascript breakpoints do not seem to work in Visual Studio 2008 when Silverlight debugging is enabled, if you un-check the Silverlight option in the debuggers section, the JavaScript breakpoint will now be hit. Not sure if it's by design or a bug...

 

Blendable Silverlight (and WPF) MVVM Applications With Dependency Injection

Introduction

One of the challenges with the idea of having separate developer and designers is how to allow the developers to write code while at the same time designers are working on the UI.

The goal is to give designers working in Expression Blend some sample data that they can data-bind to, that will match the runtime data items without requiring additional build scripts\etc.

The Model-View-ViewModel (MVVM) design pattern is a popular way to architect a SL\WPF app to provide separation of concerns, testability, etc. This article assumes a basic understanding of the MVVM pattern.

Getting Started - Defining The ViewModel

We create an interface to define out ViewModel (we can do this for every distinct view in the application). Before we do this though, we need something to represent our model data, for example a person:

     public class Person
    {
        public string Name { get; set; }
        public int Age { get; set; }
    }

Next we can define our ViewModel interface to allow the user to edit a person's details, the idea is that our view (our XAML user control) will be bound to a class that implements this interface. All data and operations are provided by the ViewModel, i.e. the view has no direct contact with the model.

     public interface IEditPersonDetailsViewModel
    {
        Person PersonToEdit { get; set; }
    }

It is this interface that allows both the designer and developer to work with a single common, well-defined set of data items.

Creating Some Design-Time Data

Now we have defined what data our view will have access to, we can proceed to provide the designer with some sample data to work with.

Remember that one of the goals for us to be able to run the application without having to modify configurations or have extra scripts, i.e. we want our app to provide a 'real' ViewModel at runtime, but a 'dummy' one to the designer in Blend. One way of providing this to to implement a version of the Service Locator design pattern:

    public class ViewModelServiceLocator
    {
        public IEditPersonDetailsViewModel GetEditPersonDetailsViewModel
        {
            get
            {
                return new EditPersonDetailsViewModel_DesignTime();
            }
        }
    }

The GetEditPersonDetailsViewModel property (by using a property we can bind to it in Blend) at the moment always returns an instance of  EditPersonDetailsViewModel_DesignTime, we will modify this shortly.EditPersonDetailsViewModel_DesignTime is defined as:

     public class EditPersonDetailsViewModel_DesignTime : IEditPersonDetailsViewModel
    {
        public EditPersonDetailsViewModel_DesignTime()
        {
            PersonToEdit = new Person()
            {
                Name="Mr Design Time Data",
                Age=44
            };
        }

        #region IEditPersonDetailsViewModel Members

        public Person PersonToEdit { get; set; }

        #endregion
    }

It is an instance of this class will provide the design time data in Blend.

Binding to Design-Time Data in Blend

(You can download a trial copy of blend from Microsoft.)

Open your project in blend and  on the Data tab choose "Define New Object Data Source...".

 

 

Select our ViewModelServiceLocator class and click OK.

 



Now we bind our usercontrol to an instance of ViewModelServiceLocator, you can do this by dragging from the data tab to [UserControl] in the Objects and Timeline pane:

 

If you look at the xaml this creates, it's basically setting the DataContext of the entire user control to the return value of the GetEditPersonDetailsViewModel property.


Now the designer can (for example) bind a textbox to the person name:



So now we have a design time ViewModel bound in Blend. If we run the app now, we would get the design time ViewModel at runtime, so the next step is to provide a 'real' ViewModel at runtime.

Providing a Runtime ViewModel

First we define our ViewModel to be used at runtime (with a hard-coded person for illustration purposes):

    public class EditPersonDetailsViewModel : IEditPersonDetailsViewModel
    {
        public EditPersonDetailsViewModel()
        {
            PersonToEdit = new Person()
            {
                Name="Mr Runtime Data",
                Age=22
            };
        }

        #region IEditPersonDetailsViewModel Members

        public Person PersonToEdit{ get; set;}

        #endregion
    }

We can then modify our ViewModel service locator to provide our runtime version:

    public class ViewModelServiceLocator
    {
        public IEditPersonDetailsViewModel GetEditPersonDetailsViewModel
        {
            get
            {
                if (System.ComponentModel.DesignerProperties.IsInDesignTool)
                    return new EditPersonDetailsViewModel_DesignTime();
                else
                    return new EditPersonDetailsViewModel();
            }
        }
    }

When we run the app we get our runtime version: 



In a real situation you could pass in the Person to be editied to the runtime viewmodel constructor and possibly use Dependency Injection to provide the dependency.

Adding Dependency Injection to the Service Locator

We can add another layer of abstraction by having the service locator delegate to a DI framework. The examples below are from another project which uses Ninject to provide DI.

    public class ViewModelServiceLocator
    {
        public IMainViewModel GetMainViewModel
        {
            get
            {
                return KernelHost.Kernel.Get<IMainViewModel>();
            }
        }



        public IAddNewWeightViewModel GetAddNewWeightViewModel
        {
            get
            {
                return KernelHost.Kernel.Get<IAddNewWeightViewModel>();
            }
        }



        public IPersonDetailsViewModel GetPersonDetailsViewModel
        {
            get
            {
                return KernelHost.Kernel.Get<IPersonDetailsViewModel>();
            }
        }
    }

 

 

    public static class KernelHost
    {
        static IKernel _kernel;
       

        static KernelHost ()
        {
            _kernel = new StandardKernel(new DI.StandardNInjectModule());
        }


        public static IKernel Kernel
        {
            get
            {             
                return _kernel;
            }
            set
            {
                _kernel = value;
            }
        }
    }

 

    public class StandardNInjectModule : StandardModule
    {
        public override void Load()
        {           
            // For normally injected IModel resolve using the instance provided by factory
            Bind<IModel>().ToFactoryMethod<IModel>(ModelFactory.Get);

            // For when a new IModel is required (and explicitly not required from thefactory)
            Bind<IModel>().To<Model>().Only(When.Context.Variable("createNewNotFromFactory").EqualTo(true));

            Bind<IModelStore>().To<IsolatedStorageModelStore>();
            Bind<IModelLoader>().To<ModelLoader>();


           
            Bind<IPerson>().To<Person>().WithArgument<string>("name", "No Name Specified").WithArgument<double>("heightCm",150);
            Bind<IBMICalculator>().To<BMICalculator>().Using<SingletonBehavior>();
            Bind<IWeightRecord>().To<WeightRecord>();
            Bind<IAggregateData>().To<AggregateData>();



            // Bind view models
            Bind<IMainViewModel>().To<MainViewModel>();
            Bind<IAddNewWeightViewModel>().To<AddNewWeightViewModel>();
            Bind<IPersonDetailsViewModel>().To<PersonDetailsViewModel>();
         

#region "Blend design time view models"
#if DEBUG
            Bind<IMainViewModel>().To<MainViewModelDesignTime>().OnlyIf(x => (System.ComponentModel.DesignerProperties.IsInDesignTool));
            Bind<IAddNewWeightViewModel>().To<AddNewWeightViewModelDesignTime>().OnlyIf(x => (System.ComponentModel.DesignerProperties.IsInDesignTool));
            Bind<IPersonDetailsViewModel>().To<PersonDetailsViewModelDesignTime>().OnlyIf(x => (System.ComponentModel.DesignerProperties.IsInDesignTool));
           
#endif
#endregion
        }
    }

 

Ensure Your Silverlight App Has Focus When The Page Loads

Assuming that your SL app is running in-browser, you may want your SL app to have initial focus, e.g. when it loads a splash screen, the OK button has focus.

You can help achieve this by using System.Windows.Browser.HtmlPage.Plugin.Focus(); in your loaded event handler for your usercontrol\childwindow, e.g.:

 

        private void ChildWindow_Loaded(object sender, RoutedEventArgs e)
        {
            // ensure SL plugin has focus
            System.Windows.Browser.HtmlPage.Plugin.Focus();
        }

Silverlight & WPF - Binding to a specific item in a collection

If you have a collection of items (for example a list of strings) that is assigned to the DataContext of a given XAML element (or parent element) you can use standard array index notation to get at a specific item in the list, just use Path=[n] where n is the item index.

For example:

<TextBlock Name="exampleBindingText" Text="{Binding Path=[3]}" />

With a DataContext set using the code:

            List<string> months = new List<string>()
            {
                "Jan", // 1st element in collection at index 0
                "Feb", // 2nd element in collection at index 1
                "Mar", // 3rd element in collection at index 2
                "Apr"  // 4th element in collection at index 3
            };

            exampleBindingText.DataContext = months;

Would result in the Text of the TextBlock outputting "Apr" as Path=[3] will get the 4th element in the collection.

Silverlight Open and Save Dialog Boxes

If you want your Silverlight app to be able to load and save data to the users file system in a location of their choice (such as My Documents, Desktop, etc.) you can use the SaveFileDialog and OpenFileDialog. This is different from reading and writing to Isolated Storage as it requires the user to interact with a dialog box, choose filename, etc. The user can also cancel out of the dialog box, which returns a value of false from the ShowDialog() method.

Below is a simple example of saving a hard-coded string "Test string to save." to a file as chosen by the user. In a live application you would want to think more carefully about possible problems arising from loading and saving, e.g. what if user loads a corrupt file, or a binary file where a text file was expected, etc. Also with the loading of files you should evaluate the security risks of allowing arbitrary data to be loaded into your application.

 

        private void btnSave_Click(object sender, RoutedEventArgs e)
        {
            SaveFileDialog dlg = new SaveFileDialog();
            dlg.Filter = "Text Files (*.txt)|*.txt";
            dlg.DefaultExt = "*.txt";

            // if the user doesn't cancel           
            if (dlg.ShowDialog() == true)
            {
                try
                {
                    // create a StreamWriter over the base stream returned by SaveFileDialog's OpenFile() method
                    using (StreamWriter writer = new StreamWriter(dlg.OpenFile()))
                    {
                        writer.Write("Test string to save.");
                        writer.Close();
                    }
                }
                catch (Exception ex) // catch all, ideally you should catch specific exceptions in most-specific to general
                {
                    MessageBox.Show(ex.ToString());
                }
            }
        }



        private void btnLoad_Click(object sender, RoutedEventArgs e)
        {
            OpenFileDialog dlg = new OpenFileDialog();
            dlg.Filter = "Text Files (*.txt)|*.txt";

            // if the user doesn't cancel
            if (dlg.ShowDialog() == true)
            {
                try
                {
                    string loadedText;
                    // create a StreamReader from OpenFileDialog's OpenText() method
                    using (StreamReader reader = dlg.File.OpenText())
                    {
                        loadedText = reader.ReadToEnd();
                        reader.Close();
                    }
                    MessageBox.Show(loadedText);
                }
                catch (Exception ex) // catch all, ideally you should catch specific exceptions in most-specific to general
                {
                    MessageBox.Show(ex.ToString());
                }
            }
        }
 

Binary Serialisation In Silverlight

One problem with the Silverlight DataContractSerializer is that it does not maintain reference links to objects. For example: if you had a customer object "Mr Green" and 2 order objects "Cooker" & "Fire" that both held references to the (single) "Mr Green", when serialised and then deserialised using DataContractSerializer the "Cooker" would point to a different "Mr Green" than the "Fire", i.e. they are not reference equal. If this is an undesirable scenario we can choose to serialise in a binary, reference-equal way so that when we deserialise there will still only be 'one' "Mr Green".

One option is to make use of a 3rd party library such as CSLA Light. You can use CSLA Light just to enable binary serialization or you could embrace the whole CSLA architecture. Either way you will probably need to change the inheritance structure of your objects and replace any properties with CSLA properties. You can find out more about CSLA Light at http://www.lhotka.net/weblog/UsingCSLALightPart1.aspx

Example of simple CSLA

 For our Customer objects to be CSLA binary serializable we would modify the class to look something like:

    [Serializable]
    public class Customer : BusinessBase<Customer>
    {

        private static PropertyInfo<string> CustomerNameProperty = RegisterProperty<string>(new PropertyInfo<string>("CustomerName", "CustomerName", string.Empty));

        public string
CustomerName
        {
            get
            {
                return GetProperty(CustomerNameProperty);   
            }
            set
            {
                
             SetProperty(CustomerNameProperty, value);   
            }
        }

We would follow a similar pattern for out order class. We could then use CSLA's MobileFormatter to serialise/deserialise (the example below defines generic methods but non-generic/typed methods could also be used):

        public static T Load<T>(Stream stream)
        {
            T m = default(T);

            Csla.Serialization.Mobile.MobileFormatter f = new Csla.Serialization.Mobile.MobileFormatter();
            m = (T) f.Deserialize(stream);

            return m;
        }


        public static void Save<T>(T obj, Stream stream)
        {
            Csla.Serialization.Mobile.MobileFormatter m = new Csla.Serialization.Mobile.MobileFormatter();
            m.Serialize(stream, obj);
        }

 

There are alternatives to CSLA Light for getting binary serialisation such as http://slserializelzo.codeplex.com/ although I've not investigated them as yet. (Hopefully a future version of Silverlight will allow the DataContractSerializer to output XML (albeit 'non standard') with referential links.

Detecting when a Silverlight app enters full screen mode

Obviously you know when a users clicks your full screen button but a the user can press escape to exit full screen mode at any time, an alternative is to respond to the FullScreenChanged event.

For example:

     public partial class MainPage : UserControl
    {
        public MainPage()
        {
            InitializeComponent();
            App.Current.Host.Content.FullScreenChanged += new EventHandler(FullScreenChanged);
        }

        void FullScreenChanged(object sender, EventArgs e)
        {
            if (App.Current.Host.Content.IsFullScreen)
                txtFullScreen.Text = "Running in full screen.";
            else
                txtFullScreen.Text = "Not running in full screen.";
        }


        private void btnFullScreen_Click(object sender, RoutedEventArgs e)
        {
            App.Current.Host.Content.IsFullScreen = true;
        }
    }

The above assumes a button to enable the user to select full screen and a textblock:

 

<UserControl x:Class="blog_misc_temp.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480">
  <Grid x:Name="LayoutRoot">
        <StackPanel>
            <TextBlock Name="txtFullScreen" Text="Not running in full screen."/>
            <Button Click="btnFullScreen_Click" Content="Enter Full Screen"></Button>
        </StackPanel>
    </Grid>
</UserControl>

 

Free Xaml drawing tool

I stumbled upon a nice (free) open source drawing application (a la Adobe Illustrator) which allows saving in SVG but also XAML! So if you don't want to/cannot afford to use Blend this could be a nice alternative, you can download from http://www.inkscape.org - should also mention you can get a 60-day evaluation copy of Blend 3 from Microsoft.

Silverlight 3 WritableBitmap pixel colour values

Rather than taking an array of Color objects, the WritableBitmap.Pixels property holds an array on ints which represent premultiplied ARGB colour values. To set a given pixel to a given colour you have to take the alpha, red, green and blue values and convert them to a premultiplied ARGB int value.

The following code contains some helpers/extension methods to make the process bit easier, for example to set every pixel to red:

for (int index = 0; index < bmp.Pixels.Length; index++)
{
   Color myColour = new Color() { A = 255, R = 255, G = 0, B = 0 };
   bmp.Pixels[index] = myColour.ToWritableBitmapPixelValue();
}

 or using the Fill extension method:

bmp.Fill( new Color() { A = 255, R = 255, G = 0, B = 0 } );

WritableBitmapHelper class listing

using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Media.Imaging;

namespace BitmapAPI
{
    /// <summary>
    /// Helper methods\extension methods for dealing with WritableBitmap,
    /// in real world implementation you'd probably choose to separate the
    /// extension methods iinto separate static classes;
    /// eg WritableBitmapExtensions & ColorExtensions
    /// </summary>
    public static class WritableBitmapHelper
    {
        /// <summary>
        /// Converts ARGB byte values to an integer
        /// Based on info found at:
        /// http://blogs.silverarcade.com/silverlight-games-101/15/silverlight-writeablebitmap-pixel-format-in-silverlight-3/
        /// </summary>
        /// <param name="alpha">Alpha transparency: 0=transparent  255=solid</param>
        /// <param name="red">Amount of red: 0 to 255</param>
        /// <param name="green">Amount of green: 0 to 255</param>
        /// <param name="blue">Amount of blue: 0 to 255</param>
        /// <returns>An integer representing an ARGB color</returns>
        public static int CalcWritableBitmapPixelValue(byte alpha, byte red, byte green, byte blue)
        {
            // Calc premultiplier once only. We need to use premultiplied ARGB32
            // rather than convential ARGB.
            double alphaPreMultiplier = alpha / 255d;

            // Premultiply rgb values with alpha
            byte r = (byte)(red   * alphaPreMultiplier);
            byte g = (byte)(green * alphaPreMultiplier);
            byte b = (byte)(blue  * alphaPreMultiplier);

            return (alpha << 24) | (r << 16) | (g << 8) | b;
        }



        /// <summary>
        /// Extension method to convert a System.Windows.Media.Color to an integer value.
        /// </summary>
        /// <param name="color">The System.Windows.Media.Color to convert</param>
        /// <returns>An integer representing the ARGB values of <paramref name="color"/></returns>
        public static int ToWritableBitmapPixelValue(this Color color)
        {
            return CalcWritableBitmapPixelValue(color.A, color.R, color.G, color.B);
        }



        /// <summary>
        /// Extension method to 'fill' a WriteableBitmap with a given solid color
        /// </summary>
        /// <param name="bmp">The WriteableBitmap to fill</param>
        /// <param name="color">The Color to fill with</param>
        public static void Fill(this WriteableBitmap bmp, Color color)
        {
            for (int i = 0; i < bmp.Pixels.Length; i++)
            {
                bmp.Pixels[i] = color.ToWritableBitmapPixelValue();
            }
        }

    }
}