All posts tagged Silverlight

In A different take on Deep Copy I talked a bit about the different approaches to how you can accomplish performing a Deep Copy of a model in C#. In this article I want to revisit the topic briefly and demonstrate two methods to handle this concern.

Q: Why not use Object.MemberwiseClone?
A: You can, but it only performs a shallow copy of your model. If you have a more complex model, you will need something that can dig deeper and map all your properties.

The first approach is our Deep Copy method done with serialization. If you have been around the Internet a bit, you will recognize it:


/// <summary>
/// Performs a Deep Copy of an object through serialization.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="oSource">The o source.</param>
/// <returns></returns>
public static T DeepCopy<T>(this T oSource)
{
    using (var ms = new MemoryStream())
    {
        var formatter = new BinaryFormatter();
        formatter.Serialize(ms, oSource);
        ms.Position = 0;
        return (T)formatter.Deserialize(ms);
    }
}

This is the cleanest way to copy a model.

1) A MemoryStream is created.
2) A BinaryFormatter serializes the source into the MemoryStream.
3) The MemoryStream position is reset for reading purposes.
4) A deserialized object is cast to type T and returned.

A simple example:


var myNewObject = myObject.DeepCopy();

 
This works great in many cases but not in a Framework like Silverlight. Given, not a lot of us use that anymore, but you could still run into a scenario where serialization is not possible. In that case, we are going to create a new instance of the model we intend to copy. Then, we will map properties between the 2 models.


/// <summary>
/// Copies all public properties from one class to another.
/// </summary>
/// <param name="source">The source.</param>
/// <param name="destination">The destination.</param>
/// <exception cref="System.Exception">Source and destination cannot be null and must be 
/// of the same type!</exception>
public static void MapProperties<T>(this T source, T destination) where T : class
{
    // Source and destination must exist.
    if ((source == null || destination == null)) return;

    // Get properties
    var propertyInfos = source.GetType().GetProperties();
    if (!propertyInfos.Any()) return;

    // Process only public properties
    foreach (var propInfo in propertyInfos.Where(x => x.CanWrite))
    {
        // Get value from source and assign to destination.
        var value = propInfo.GetValue(source, null);
        if (value == null) continue;

        // Evaluate
        var valType = value.GetType();

        // Collections
        if (valType.InheritsFrom<ICollection>())
        {
            var sourceCollection = value as IList;
            if (sourceCollection == null) continue;

            // Create new instance of collection
            IList destinationCollection = null;
            destinationCollection = (valType.BaseType == typeof(Array))
                ? Array.CreateInstance(valType.GetElementType(), sourceCollection.Count)
                : (IList)Activator.CreateInstance(valType, null);
            if (destinationCollection == null) continue;

            // Map properties
            foreach (var item in sourceCollection)
            {
                // New item instance
                var newItem = HasDefaultConstructor(item.GetType()) 
                    ? Activator.CreateInstance(item.GetType(), null) 
                    : item;

                // Map properties
                item.MapProperties(newItem);

                // Add to destination collection
                if (valType.BaseType == typeof(Array))
                {
                    destinationCollection[sourceCollection.IndexOf(item)] = newItem;
                }
                else
                {
                    destinationCollection.Add(newItem);
                }
            }

            // Add new collection to destination
            propInfo.SetValue(destination, destinationCollection, null);
        }
        else
        {
            propInfo.SetValue(destination, value, null);
        }

        // Check for properties and propagate if they exist.
        var newPropInfos = value.GetType().GetProperties();
        if (!newPropInfos.Any()) continue;

        // Copy properties for each child where necessary.
        var childSource = source.GetType().GetProperty(propInfo.Name);
        var childDestination = destination.GetType().GetProperty(propInfo.Name);
        childSource.MapProperties(childDestination);
    }
}

/// <summary>
/// Determines whether the type has a default contructor.
/// </summary>
/// <param name="type">The type.</param>
/// <returns></returns>
private static bool HasDefaultConstructor(Type type)
{
    return
        type.GetConstructor(Type.EmptyTypes) != null ||
        type.GetConstructors(BindingFlags.Instance | BindingFlags.Public)
            .Any(x => x.GetParameters().All(p => p.IsOptional));
}

Here is what is going on:

1) We get the properties associated with the class type.
2) We evaluate the public properties.
3) Using reflection, we retrieve the value from the source and apply it to the destination.
4) If the value is not null, we drill down further for more public properties recursively.
Note: Collections need a bit of extra work to ensure we don’t simply copy over their object instances.
5) Repeat until the entire model has been traversed.

A simple example:


SomeType myNewObject = new SomeType();

// Assumption: "myObject" is of type "SomeType"
myObject.MapProperties(myNewObject);

In conclusion: if you can’t serialize, map the properties between the 2 models.

That’s all for now.

Welcome to Part 2 of our series Making a Better ObservableCollection. If you missed Making a Better ObservableCollection Part 1 – Extensions you can get to it here.

In this next section I am going to share a version of my ObservableCollectionEx that allows cross-threading. The idea here is to have an ObservableCollection which you can update from an Async thread so as not to impact the owning thread. This is especially useful in WPF when you don’t wish to block the UI thread while performing collection updates.

Let’s see the code:


/// <summary>
/// Initializes a new instance of the 
/// <see cref="ObservableCollectionEx{T}"/> class.
/// </summary> 
public class ObservableCollectionEx<T> : ObservableCollection<T>
{
    #region Constructors

    /// <summary>
    /// Initializes a new instance of the
    /// <see cref="ObservableCollectionEx{T}" /> class.
    /// </summary>
    public ObservableCollectionEx()
    {
    }

    ///
    /// Initializes a new instance of the
    ///  class.
    ///
    ///The collection.
    public ObservableCollectionEx(IEnumerable<T> collection) : this()
    {
        this.AddRange(collection);
    }

    #endregion

    #region Events

    /// <summary>
    /// Source: New Things I Learned
    /// Title: Have worker thread update ObservableCollection that is bound to a ListCollectionView
    /// http://geekswithblogs.net/NewThingsILearned/archive/2008/01/16/have-worker-thread-update-observablecollection-that-is-bound-to-a.aspx
    /// Note: Improved for clarity and the following of proper coding standards.
    /// </summary>
    /// <param name="e"></param>
    protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
    {
        // Use BlockReentrancy
        using (BlockReentrancy())
        {
            var eventHandler = CollectionChanged;
            if (eventHandler == null) return;

            // Only proceed if handler exists.
            Delegate[] delegates = eventHandler.GetInvocationList();

            // Walk through invocation list.
            foreach (var @delegate in delegates)
            {
                var handler = (NotifyCollectionChangedEventHandler)@delegate;
                var currentDispatcher = handler.Target as DispatcherObject;

                // If the subscriber is a DispatcherObject and different thread.
                if ((currentDispatcher != null) &amp;&amp; (!currentDispatcher.CheckAccess()))
                {
                    // Invoke handler in the target dispatcher's thread.
                    currentDispatcher.Dispatcher.Invoke(
                        DispatcherPriority.DataBind, handler, this, e);
                }

                else
                {
                    // Execute as-is
                    handler(this, e);
                }
            }
        }
    }

    /// <summary>
    /// Overridden NotifyCollectionChangedEventHandler event.
    /// </summary>
    public override event NotifyCollectionChangedEventHandler CollectionChanged;

    #endregion
}

The constructors are pretty straight forward. We want to have an empty constructor and one that allows an immediate “AddRange” of an IEnumerable just like List and ObservableCollection allow, but that is not really the point of this post.

The main feature here is a slightly reformatted version of a wonderful post from a blog called New Things I Learned which covers cross-thread access with an ObservableCollection.

So, what does this code do?

  1. First we use BlockReentracy to prevent changes to the collection while we are evaluating it.
  2. Next, we get the Invocation List from the CollectionChanged event handler. This is the list of delegates subscribing to the event.
  3. Evaluate each delegate by retrieving it’s Target and casting it to a DispatcherObject.
  4. Make sure the DispatcherObject is valid and that the current thread has access.
  5. Invoke the delegate in the DispatcherObject’s thread.

It’s not the simplest code to follow when you aren’t overly familiar with working with event delegates but it gets the job done quite well.

Next time, we will talk about using a custom SortComparer to improve DataGrid performance.

Until next time.

One of the bigger annoyances dealing with programming in Silverlight is the deployment of XAP files. In order to properly update a XAP file you typically:

  1. Rename XAP file to a ZIP file.
  2. Extract the ServiceReferences.ClientConfig file.
  3. Update the configuration file with the proper IP information.
  4. Update the ZIP file and save.
  5. Rename ZIP file back to XAP.

Doesn’t that sound stupid? I think so. What the hell was Microsoft thinking?

Q: So, how do we do that with code so we can skip this frustration?
A: Let’s first look at a few factors:

  • We are using the .Net 4.0 Framework.
  • Don’t bother using System.IO.Packaging.ZipPackage. It thinks XAP files are always corrupt. It’s annoying.
  • We are just updating the IP information.

To solve the ZIP file library issue, I turned to DotNetZip: http://dotnetzip.codeplex.com/. It’s by far one of the most convenient libraries out there and it’s free.

First, let’s look at how we update the configuration file if it was committed to a MemoryStream. In this snippet we:

  1. Grab all the contents from the MemoryStream.
  2. Replace the IP information in the content.
  3. Clear the MemoryStream.
  4. Overwrite the stream contents with the new content.
  5. Reset the position in the stream to 0.

/// <summary>
/// Updates the configuration file.
/// </summary>
/// <param name="stream">The stream.</param>
/// <param name="replacementIp">The replacement ip.</param>
/// <param name="destinationIp">The destination ip.</param>
/// <returns></returns>
private bool UpdateConfigurationFile(MemoryStream stream,
    string replacementIp, string destinationIp)
{
    bool isSuccessful = false;

    try
    {
        // Read current file
        var reader = new StreamReader(stream);
        stream.Position = 0;
        var contents = reader.ReadToEnd();
                
        // Update IP information
        var newContents = contents.Replace(replacementIp, destinationIp);

        // Reset contents of stream
        stream.SetLength(0);

        // Overwrite original configuration file
        var writer = new StreamWriter(stream);
        writer.Write(newContents);
        writer.Flush();

        // Set position in stream to 0.
        // This allows us to start writing from the beginning of the stream.
        stream.Seek(0, SeekOrigin.Begin);

        // Success
        isSuccessful = true;
    }
    catch (Exception)
    {
    }

    // return
    return isSuccessful;
}

Our main method below does this:

  1. Extract the ServiceReferences.ClientConfig file.
  2. Call the UpdateConfigurationFile method above to revise the IP information.
  3. Update the ZIP file and commit the changes.

/// <summary>
/// Updates the silverlight configuration file.
/// </summary>
/// <param name="configFileName">Name of the config file.</param>
/// <param name="xapFilePath">The xap file path.</param>
/// <param name="replacementIp">The replacement ip.</param>
/// <param name="destinationIp">The destination ip.</param>
/// <returns></returns>
private bool UpdateSilverlightConfigurationFile(string configFileName,
    string xapFilePath, string replacementIp, string destinationIp)
{
    // Check file path
    if (!File.Exists(xapFilePath)) { return false; }

    // Open XAP and modify configuration
    using (var zip = ZipFile.Read(xapFilePath))
    {
        // Get config file
        var entry = zip[configFileName];
        var stream = new MemoryStream();
        entry.Extract(stream);

        // Manipulate configuration
        var updated =
            UpdateConfigurationFile(stream, replacementIp, destinationIp);

        // Evaluate
        if (updated)
        {
            // Replace existing configuration file in XAP
            zip.UpdateEntry(configFileName, stream);
            zip.Save();
        }
    }

    // return
    return true;
}

So, let’s look at the code in it’s entirety so that we get an implementation example as well as the needed includes:


using System;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using Ionic.Zip;

namespace XAPFileUpdaterTest
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainPage : UserControl
    {
        public MainPage()
        {
            // Intialize UI
            InitializeComponent();

            // Parameters
            string configFile = "ServiceReferences.ClientConfig";
            string xap = @"MyAwesomeApp.xap";
            string replacementIp = "127.0.0.1";
            string destinationIp = "12.23.45.67";

            // Check
            var isClientConfigUpdated =
                UpdateSilverlightConfigurationFile(
                   configFile, xap, replacementIp, destinationIp);

            // Setup message
            var message =
                (isClientConfigUpdated) ? "was successful" : "failed";

            // Notify user
            MessageBox.Show("The current update " + message);
        }

        /// <summary>
        /// Updates the configuration file.
        /// </summary>
        /// <param name="stream">The stream.</param>
        /// <param name="replacementIp">The replacement ip.</param>
        /// <param name="destinationIp">The destination ip.</param>
        /// <returns></returns>
        private bool UpdateConfigurationFile(MemoryStream stream,
            string replacementIp, string destinationIp)
        {
            bool isSuccessful = false;

            try
            {
                // Read current file
                var reader = new StreamReader(stream);
                stream.Position = 0;
                var contents = reader.ReadToEnd();

                // Update IP information
                var newContents = contents.Replace(replacementIp, destinationIp);

                // Reset contents of stream
                stream.SetLength(0);

                // Overwrite original configuration file
                var writer = new StreamWriter(stream);
                writer.Write(newContents);
                writer.Flush();

                // Set position in stream to 0.
                // This allows us to start writing from the beginning of the stream.
                stream.Seek(0, SeekOrigin.Begin);

                // Success
                isSuccessful = true;
            }
            catch (Exception)
            {
            }

            // return
            return isSuccessful;
        }

        /// <summary>
        /// Updates the silverlight configuration file.
        /// </summary>
        /// <param name="configFileName">Name of the config file.</param>
        /// <param name="xapFilePath">The xap file path.</param>
        /// <param name="replacementIp">The replacement ip.</param>
        /// <param name="destinationIp">The destination ip.</param>
        /// <returns></returns>
        private bool UpdateSilverlightConfigurationFile(string configFileName,
            string xapFilePath, string replacementIp, string destinationIp)
        {
            // Check file path
            if (!File.Exists(xapFilePath)) { return false; }

            // Open XAP and modify configuration
            using (var zip = ZipFile.Read(xapFilePath))
            {
                // Get config file
                var entry = zip[configFileName];
                var stream = new MemoryStream();
                entry.Extract(stream);

                // Manipulate configuration
                var updated =
                    UpdateConfigurationFile(stream, replacementIp, destinationIp);

                // Evaluate
                if (updated)
                {
                    // Replace existing configuration file in XAP
                    zip.UpdateEntry(configFileName, stream);
                    zip.Save();
                }
            }

            // return
            return true;
        }
    }
}

With the help of DotNetZip we were able to update the XAP file with our revised IP information.

That’s all for now. I hope I helped you with this annoyance.