WinRT/C#: DataPackage and custom objects

Ok, after some intensive implementation weeks, I think I should blog some articles about tips’n’tricks when developing Windows 8 Metro apps with WinRT and C#. This first article is about the DataPackage class. You need to get in touch with this class when you want to exchange data from app to app by implementing the Share contract or when you want to implement custom drag&drop functionality in your own app. DataPackage has built-in functionality for exchanging a variety of document formats: Bitmap, Html, Rtf, Text, Uri and StorageItems (files and folders).

Especially in the drag&drop scenario these standard data formats often aren’t enough. Imagine a grouped ListView or GridView. Because of the grouping you have to implement drag&drop manually in WinRT Metro apps. Now if you want to exchange data between the drag event and the drop event, you often want to fill the DataPackage with data objects of your custom data types. Unfortunately, that’s not possible out of the box with the DataPackage. Only scalar types, implementations of IRandomAccessStream or IStorageItem and IUri are supported.

So, there’s no possibility to write the real instances of your objects into a data package in the drag event and read them in the drop event. But here’s the solution: serialization and writing into an IRandomAccessStream. While you serialize and deserialize your object, you won’t get the original instance in the drop event, but if it’s ok for you, then this method works fine.

I’ve written some extensions for DataPackage and IRandomAccessStream, that you can use to get this task really quickly done:

public static class IRandomAccessStreamExtensions
{
    public static async void WriteObject<T>(this IRandomAccessStream stream, T obj)
    {
        var serializer = new DataContractSerializer(typeof(T));
        serializer.WriteObject(stream.AsStreamForWrite(), obj);
    }

    public static T ReadObject<T>(this IRandomAccessStream stream)
    {
        var serializer = new DataContractSerializer(typeof(T));
        return (T)serializer.ReadObject(stream.GetInputStreamAt(0).AsStreamForRead());
    }
}

public static class DataPackageExtensions
{
    public static void SetObject<T>(this DataPackage data, string formatId, T obj)
    {
        var randomAccessStream = new InMemoryRandomAccessStream();
        randomAccessStream.WriteObject(obj);
        data.SetData(formatId, randomAccessStream);
    }

    public static async Task<T> GetObjectAsync<T>(this DataPackage data, string formatId)
    {
        var randomAccessStream = await data.GetView().GetDataAsync(formatId) as IRandomAccessStream;
        if ((randomAccessStream == null) || (randomAccessStream.Size == 0) || !randomAccessStream.CanRead)
        {
            return default(T);
        }

        return randomAccessStream.ReadObject<T>();
    }
}

Now you’re able to use the SetObject() and GetObjectAsync() extension methods on DataPackage to easily store and retrieve items to/from a data package in the drag/drop event or in your source/target implementation of the Share contract:

private void OnGridViewDragItemsStarting(object sender, DragItemsStartingEventArgs e)
{
    e.Data.SetObject("MyDataTypeList", e.Items.OfType<MyDataType>().ToList());
}

private async void OnGridViewDrop(object sender, DragEventArgs e)
{
    var myDataItems = await e.Data.GetObjectAsync<List<MyDataType>>("MyDataTypeList");
}

The string parameter „formatId“ specifies the identifier for your data format in the DataPackage. Note how the IRandomAccessStreamExtensions methods use the DataContractSerializer for serialization and deserialization. This means that your data class has to be decorated with the DataContract and DataMember attributes to specify which data should be serialized.

I hope you can use this little helper in some cases when working with the DataPackage for sharing data. In some next blog post I want to show you how to manually implement drag&drop in your grouped ListView and GridView in WinRT Metro apps. Stay tuned!

kick it on DotNetKicks.com