Creating a FluidBanner control using Windows Composition


A few days ago, I came across the following tweet which showed a banner of images with a complex set of animations during user interactions. It was created for the web using CSS.

I wondered if the same could be achieved in Windows UWP applications using Composition APIs. After tinkering for a few days, I came up with the FluidBanner control.



In this post I will be describing in detail about the FluidBanner control.

FluidBanner Dependency Properties

FluidBanner has the following dependency properties which allow the user to customize it.

Dependency Property Type Description Default Value
DecodeHeight int The height, in pixels, that the images are decoded to. (Optional) 0
DecodeWidth int The width, in pixels, that the images are decoded to. (Optional) 0
ItemBackground Color The background color of each item in the FluidBanner Black
ItemGap double The gap between adjacent items in the banner. 30
ItemsSource IEnumerable<Uri> The collection of Uris of images to be shown in the FluidBanner null
Padding Thickness The padding inside the FluidBanner Thickness(0)

Demystifying the FluidBanner Animations

For each of the animations in the FluidBanner, a key role is played by the the Clip property (which is an instance of the InsetClip class) of the visual. According to MSDN, the InsetClip clips a portion of a visual. The visible portion of the visual is a rectangle defined as inset values from the edges of the visual. The portion of the visual outside the rectangle is clipped.



Most of the animations within the FluidBanner is accomplished by animating the LeftInset and RightInset properties of the InsetClip class.

Digging deeper into the FluidBanner

FluidBanner Visual States

Based on FluidBanner’s behavior during user interaction, its visual states can be categorized into

    • Unselected state – In this state, which is also the initial state, none of the images are selected by the user and each image is represented by a BannerItem which is a vertical slice of the image. (I will provide more details about BannerItem later in this blog.) Adjacent BannerItems are separated by a predefined set of pixels (determined by the ItemGap property).


  • Selected state – In this state, the user has selected an image by clicking or tapping on it and the selected BannerItem animates and expands to fill the total area of the FluidBanner. The rest of the BannerItems scale back and fade out.


FluidBanner Layout

FluidBanner derives from the Panel class and it means that the responsibility to arrange and render its children lies solely on the FluidBanner. Before describing how FluidBanner renders its children, it is important to understand how the Layout Pass works.

Layout Pass

According to MSDN, the Layout Pass describes the process of measuring and arranging the members of a Panel-derived object’s collection of children, and then drawing them onscreen. The layout pass is a mathematically-intensive process‚ÄĒthe larger the number of children in the collection, the greater the number of calculations required. At its simplest, layout is a recursive system that leads to an element being sized, positioned, and drawn onscreen.

Sounds complicated? Well, let me explain it in simpler terms. The Layout Pass can be thought of like a communication between the layout container and its children. This communication takes place in two stages which are often referred to as passes.

In the first pass, the parent informs each child about the size available for rendering (based on constraints such as Margin, Padding, Alignment etc) and asks how much space the child needs to render itself. Before a child informs the parent, how much space it needs, the child asks all of its children how much space they need. (This scenario is valid in case of nested panels). This goes on recursively until all the children have been queried. Once each child calculates the space required, its DesiredSize property is set.

At the end of the first pass, once all the children respond, the parent can determine its required size by aggregating the DesiredSize properties of its children according to its own layout rules. This pass is called the Measure pass.

In the second pass, the parent informs each child the final size that has been allocated to it and the child must render itself in that size. The child, in turn, calculates the final size for each of its child (based on its own layout rules) and informs them accordingly. This happens recursively until all the children have been notified. This pass is called the Arrange pass.



The Measure pass is characterized by the Measure() call made by the parent which invokes the MeasureOverride() method on its children. The Arrange pass is characterized by the Arrange() method call made by the parent which then invokes the ArrangeOverride() method on its children.

protected override Size MeasureOverride(Size availableSize);
protected override Size ArrangeOverride(Size finalSize);

The actual arrangement of children happens during the Arrange pass. It is important to understand that the availableSize in the MeasureOverride() method might not be same as the finalSize in the ArrangeOverride() method.

Layout Pass in FluidBanner

Measure Pass

In the Measure pass, since the panel will accommodate all sizes of images, there is no need to explicitly calculate the size of the its children. So the FluidBanner is happy with whatever size is available.

However, one crucial thing to note is that if you are implementing a custom control and handling the Layout passes yourself, then MeasureOverride method is the best place to initialize your Composition related objects (Compositor, Visuals, Animations etc) which you may be using throughout your application. (Make sure that you initialize them only once. You can accomplish this by checking whether the Compositor reference is already initialized.). Layout Pass happens before the Loaded event is raised.

Visual _rootVisual;
Compositor _compositor;

protected override Size MeasureOverride(Size availableSize)
    if (_compositor != null)
        // Initialize Composition related elements


public void InitializeComposition()
    _rootVisual = ElementCompositionPreview.GetElementVisual(this);
    _compositor = _rootVisual.Compositor;
Arrange Pass

As previously mentioned, Arrange pass is responsible for the arrangement of children within the parent. The following factors influence the arrangement of the BannerItemsPadding, the finalSize provided and the number of images to be displayed. Based on these factors, the size and offset of each BannerItem is calculated and each BannerItem created and added to the visual tree.


Ok, now lets talk about BannerItem in detail. BannerItem represents a single image displayed in the FluidBanner (whether in Selected or Unselected state).

BannerItem comprises of a three visuals –

  • contentVisual – It is a SpriteVisual which hosts the image loaded from the Uri.
  • bgVisual – It is a SpriteVisual filled with the color specified by the ItemBackground property (default Black). It serves as a background for the image if the image does not stretch to occupy the entire BannerItem area.
  • container – It is a ContainerVisual which hosts the contentVisual and the bgVisual. The Clip property of the container is set to an InsetClip whose LeftInset, RightInset, TopInset & BottomInset¬†properties determine the size of the image slice displayed in the BannerItem. The BannerItem size is expanded and collapsed by animating these properties.


Layers and Animations

At the root of the FluidBanner’s visual tree is a ContainerVisual called _rootContainer. In order to accommodate¬†its two visual states, the _rootContainer hosts two LayerVisuals_bgLayer and _topLayer. As the name suggests, _topLayer is rendered above the _bgLayer.

In the Unselected state, the _bgLayer hosts all the BannerItems. Clicking on any of the BannerItems will cause the FluidBanner to transition to the Selected state. During this transition, the selected BannerItem is moved to the _topLayer and its Clip animated to move to the left most position in the FluidBanner. After that the Clip is expanded to occupy the full surface area of the FluidBanner. Meanwhile the remaining BannerItems in the _bgLayer scale back and fade out.

Clicking or tapping on the expanded selected BannerItem will cause the FluidBanner to transition to the Unselected state. During this transition, the Clip of the selected BannerItem collapses to occupy the left most position in the FluidBanner and then it moves back to its original location in the _bgLayer. Meanwhile the remaining BannerItems in the _bgLayer fade in and scale up to normal size.


Pointer Interactions

Pointer interactions are handled by subscribing to the PointerMoved and PointerPressed events in the FluidBanner. Since the visuals themselves do not support pointer interactions, the location of each of the BannerItems is kept track of and based on the Pointer event raised, the corresponding BannerItem under the Pointer is obtained and the appropriate animations initiated.

Source Code

The source code for FluidBanner is available in GitHub.

WPFSpark v1.3 Released

Yesterday (Jul 10th, 2016), I released WPFSpark v1.3 on GitHub & NuGet. In this version I have updated the Layout logic of FluidWrapPanel with a more robust code. Now it rearranges the variable sized children in a more efficient manner.

Also, I upgraded the target DotNet Framework of the WPFSpark project from 4.6.1 to 4.6.2.

If you are installing this version in your project, please ensure that your project is also targeting the version 4.6.2 of the DotNet Framework.

Retiring WPFSpark.UWP project

I will be retiring the WPFSpark.UWP project soon. Having controls used for UWP project and keeping the project name “WPFSpark” did not make sense (as WPF is limited to Windows 10 desktop projects). As of now WPFSpark.UWP project contains only the FluidWrapPanel control. I will be moving the control (and its source code) to the CompositionProToolkit project.


CompositionProToolkit is a collection of helper classes for Windows.UI.Composition. It also contains the FluidProgressRing control which is a concept design for a modern version of the ProgressRing control in UWP.


I have currently ported the FluidWrapPanel code in WPFSpark v1.3 to UWP and now I am trying to use Windows.UI.Composition APIs to replace the XAML animations used in FluidWrapPanel. This would result in smoother animations in FluidWrapPanel control. This work is currently in progress and I hope to release it soon.


In this post, I will be introducing you to another project of mine which I started this month in GitHub – CompositionExpressionToolkit.


Since Build 2016, I have started looking into Windows.UI.Composition APIs. These APIs are present in a layer between the XAML layer and the DirectX layer. They provide more powerful features than the XAML layer and also have a less steep learning curve than the DirectX layer. Composition APIs help to make your UI faster and more fluid. They definitely add a wow factor to your UI which might have not been possible with coding entirely in XAML.

If you are still unconvinced about Composition APIs, read the Why even use Composition Section in this blog. (TL;DR Composition APIs work outside your application process and will continue to render animations at 60 FPS even if your UI thread is blocked!)

So you might be wondering where CompositionExpressionToolkit fits in. Well, according to MSDN, ExpressionAnimation and KeyFrameAnimation use a mathematical expression to specify how the animated value should be calculated each frame. The expressions can reference properties from composition objects. Currently, the mathematical expression is provided in the form of a string. Expression animations work by parsing the mathematical expression string and internally converting it to a list of operations to produce an output value.

Well, using a string for creating an expression increases the chance of introducing errors (spelling, type-mismatch to name a few). These errors will not be picked up during compile time and can be difficult to debug during runtime too.

To mitigate this issue, we can use lambda expressions which are statically typed and allow the common errors to be caught during compile time. But how do you convert them to the appropriate string format to pass it as an input to the ExpressionAnimation / KeyFrameAnimation? This is where CompositionExpressionToolkit fits in. It provides several extension methods which take in a lambda expression as input and convert it to an appropriate string format which will be accepted by the ExpressionAnimation / KeyFrameAnimation.

CompositionExpressionToolkit also provides several other extension methods and helper classes which make it easier to work with Windows.UI.Composition.

Check out the CompositionExpressionToolkit GitHub page to know more details.

If you want to try out CompositionExpressionToolkit in your own project, it is available in NuGet too. To install CompositionExpressionToolkit, run the following command in the Package Manager Console in Visual Studio

Install-Package CompositionExpressionToolkit

Note: The current version of CompositionExpressionToolkit (v0.1.7.1) requires Windows SDK Preview 14332.

If you want to learn more about Windows.UI.Composition concepts then the following links will give you a good start –

Dynamic FluidWrapPanel using AdaptiveTriggers

A few days ago, I was asked if the FluidWrapPanel could resize its children uniformly to use the maximum available space. My initial response was that FluidWrapPanel acts as a container and does not influence its children’s sizes. If it resized its children’s size to a very small size then it may make the content unreadable.

Then I got further details of the requested scenario – The FluidWrapPanel has 12 children. When the size of the FluidWrapPanel changes the layout of the children should also change (they should also be resized to occupy the maximum available space). For example when the FluidWrapPanel is displayed in Portrait mode (having a certain width), the children should be displayed in a 6 x 2 layout, when in Landscape mode (having medium size) they should be displayed in 4 x 3 layout and when in Landscape mode (with maximum width) the layout should change to 3 x 4. All this should happen without affecting the fluidity of the FluidWrapPanel.


Well, let me explain FluidWrapPanel’s behavior in this scenario. FluidWrapPanel will not know whether the children should be displayed in a 6 x 2 layout, a 4 x 3 layout or in a single row. Its logic for arranging its children is governed by the following factors

  • ItemWidth dependency property
  • ItemHeight dependency property

If the child’s width and height are less than the ItemWidth and ItemHeight respectively, there will be a gap between adjacent children. If they are more, the adjacent children with overlap each other.

So the solution is to listen to the SizeChanged event of the FluidWrapPanel and when it occurs

  • Calculate which layout needs to be displayed in the FluidWrapPanel
  • Based on the layout and the size of the FluidWrapPanel, calculate the desired item width and height.
  • Set each of the child’s width and height to the calculated item width and height respectively.
  • Set the ItemWidth and ItemHeight dependency properties of the FluidWrapPanel to the calculated item width and height respectively.

This solution works best when all the children are of same size i.e. having width and height equal to the ItemWidth and ItemHeight respectively.

Since this solution is applicable mainly in UWP applications, I thought it was best to utilize AdaptiveTriggers to solve this issue with ease.

I made a sample UWP project to implement this solution. Here is what I did.

1. Create a New Universal Project and add reference to WPFSpark.UWP NuGet package.

2. Create a UserControl called FluidItemControl which will be a child to the FluidWrapPanel. It just consists of a border (having random background) and a textblock (showing a number).

I came upon a strange behavior of Page class. If you add a new dependency property to MainPage.xaml.cs and try to bind to it in MainPage.xaml, then that property will not be recognized. After trying out several ways unsuccessfully, I found a workaround in StackOverflow. It seems you have to define a base class, say PageBase, which derives from Page and then define your dependency property there. Then MainPage should derive from PageBase. Now the binding will work. Strange but true!
3. So here is the definition of PageBase class.
public class PageBase : Page
    public enum PageDisplayType
        None = 0,
        Display3x4 = 1,
        Display4x3 = 2,
        Display6x2 = 3

    #region PageDisplay
    /// <summary>
    /// PageDisplay Dependency Property
    /// </summary>
    public static readonly DependencyProperty PageDisplayProperty =
        DependencyProperty.Register("PageDisplay", typeof(PageDisplayType), typeof(MainPage),
            new PropertyMetadata(PageBase.PageDisplayType.None, OnPageDisplayChanged));

    /// <summary>
    /// Gets or sets the PageDisplay property. This dependency property 
    /// indicates the number of rows and columns to set in the FluidWrapPanel.
    /// </summary>
    public PageBase.PageDisplayType PageDisplay
        get { return (PageBase.PageDisplayType)GetValue(PageDisplayProperty); }
        set { SetValue(PageDisplayProperty, value); }

    /// <summary>
    /// Handles changes to the PageDisplay property.
    /// </summary>
    /// <param name="d">MainPage</param>
    /// <param name="e">DependencyProperty changed event arguments</param>
    private static void OnPageDisplayChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        var page = (PageBase)d;
        var oldPageDisplay = (PageBase.PageDisplayType)e.OldValue;
        var newPageDisplay = page.PageDisplay;
        page.OnPageDisplayChanged(oldPageDisplay, newPageDisplay);

    /// <summary>
    /// Provides derived classes an opportunity to handle changes to the PageDisplay property.
    /// </summary>
    /// <param name="oldPageDisplay">Old Value</param>
    /// <param name="newPageDisplay">New Value</param>
    async void OnPageDisplayChanged(PageBase.PageDisplayType oldPageDisplay, PageBase.PageDisplayType newPageDisplay)
        switch (newPageDisplay)
            case PageBase.PageDisplayType.Display3x4:
                rows = 3;
                columns = 4;
            case PageBase.PageDisplayType.Display4x3:
                rows = 4;
                columns = 3;
            case PageBase.PageDisplayType.Display6x2:
                rows = 6;
                columns = 2;
                throw new ArgumentOutOfRangeException(nameof(newPageDisplay), newPageDisplay, null);

        await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, RefreshPanel);


    protected double rows = 0;
    protected double columns = 0;

    protected virtual void RefreshPanel()

4. I defined the AdaptiveTriggers in MainPage.xaml. These triggers will change the PageDisplay dependency property based on the width of the MainPage.

<local:PageBase x:Class="TestFWPanel.MainPage"
        <x:Double x:Key="SmallScreenWidth">0</x:Double>
        <x:Double x:Key="MediumScreenWidth">540</x:Double>
        <x:Double x:Key="LargeScreenWidth">900</x:Double>
    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
                <VisualState x:Name="Small">
                        <AdaptiveTrigger MinWindowWidth="{StaticResource SmallScreenWidth}" />
                        <Setter Target="RootPage.PageDisplay"
                <VisualState x:Name="Medium">
                        <AdaptiveTrigger MinWindowWidth="{StaticResource MediumScreenWidth}" />
                        <Setter Target="RootPage.PageDisplay"
                                Value="Display4x3" />
                <VisualState x:Name="Large">
                        <AdaptiveTrigger MinWindowWidth="{StaticResource LargeScreenWidth}" />
                        <Setter Target="RootPage.PageDisplay"
                                Value="Display3x4" />
        <Grid x:Name="ContainerGrid"
            <wpfSpark:FluidWrapPanel x:Name="fwPanel"

5. The size of the FluidWrapPanel also changes when the size of the MainPage changes. I am calling the RefreshPanel() method when this happens. This method updates the ItemWidth and ItemHeight properties along with the sizes of the children accordingly.

protected override void RefreshPanel()
    if ((rows.IsZero()) || (columns.IsZero()))

    var width = Math.Floor(fwPanel.Width/columns);
    var height = Math.Floor(fwPanel.Height/rows);

    foreach (var child in fwPanel.FluidItems.OfType<FluidItemControl>())
        child.Width = width;
        child.Height = height;

    fwPanel.ItemWidth = width;
    fwPanel.ItemHeight = height;

You can find the full source code here.

WPFSpark v1.2.1.1 Released


Hello everyone. An update to WPFSpark has been released. WPFSpark v1.2.1.1 brings an update which allows ToggleSwitch control to show the current Windows Accent Color. It even dynamically updates its color when the user changes the  Accent Color.

For this purpose, I have split the ToggleSwitch Control Template into two РBasic and Modern. The ToggleSwitchBasicTemplate is to be used when you are displaying text within the ToggleSwitch control for the Checked and Unchecked state.


There is a predefined style using the ToggleSwitchBasicTemplate which can be used like this

	Style="{StaticResource {ComponentResourceKey TypeInTargetAssembly=wpfspark:ToggleSwitch, ResourceId=ToggleSwitch.Basic.Style}}" />

ToggleSwitchModernTemplate is to be used to emulate the look and feel of the toggle switch in Windows 10 Mobile and iOS.

There are three predefined styles using the ToggleSwitchModernTemplate¬†–

  • ToggleSwitch.UWP.Light.Style – UWP Style ToggleSwitch for light background.
	Style="{StaticResource {ComponentResourceKey TypeInTargetAssembly=wpfspark:ToggleSwitch, ResourceId=ToggleSwitch.UWP.Light.Style}}" />
  • ToggleSwitch.UWP.Dark.Style – UWP Style ToggleSwitch for light background.
	Style="{StaticResource {ComponentResourceKey TypeInTargetAssembly=wpfspark:ToggleSwitch, ResourceId=ToggleSwitch.UWP.Dark.Style}}" />
  • ToggleSwitch.iOS.Light.Style – iOS Style ToggleSwitch for light background.
	Style="{StaticResource {ComponentResourceKey TypeInTargetAssembly=wpfspark:ToggleSwitch, ResourceId=ToggleSwitch.iOS.Light.Style}}" />

WPFSpark.UWP : Creating a single NuGet package containing x86, x64 and ARM binaries


Hello again! I¬†have another good news to share with you. WPFSpark.UWP v1.2 is released today.ūüôā . You can¬†get the NuGet package from here. You can view the source code here. In this blog I will explain in detail how I created the NuGet package for WPFSpark.UWP.
Update(Jul 14, 2016): I have updated this article to mention that the nuget.exe file should be placed in the root folder of the project (not in the NuGet folder). If you place the nuget.exe file in the NuGet folder then it will also be packaged within your Nuget package, thus increasing your Nuget package size drastically (~1.5 MB).
If you have already created your directory structure, you just have to move the nuget.exe file from the NuGet folder to the root folder of the project and modify the CreateNugetPackage.cmd file accordingly.
For the past few weeks I have been trying to port the WPFSpark library to the Universal Windows Platform (UWP). Since most of the controls in WPFSpark are already present natively (like ToggleSwitch, Inderminate ProgressBar, ProgressRing to name a few), porting some of the controls would create redundant controls in UWP. So the only control eligible for porting to UWP from WPF was the FluidWrapPanel.
I was able to port the control with minimal changes. The FluidMouseDragBehavior has been renamed to FluidPointerDragBehavior. A reference to the Microsoft.Xaml.Behaviors.Uwp.Managed NuGet package was added for the Behavior class from which FluidPointerDragBehavior is derived.
Ok, now the control has been successfully ported and it’s time to create the NuGet package. However, I wanted to create a NuGet package which contained all the three binaries – x86, x64 & ARM. I did not want to create separate binaries for each and upload them to NuGet. I wanted to have a single NuGet package which, when referenced by a project, would add reference to the appropriate binary based on the platform for which the project is being built.
The search for an apt solution led me here. I tested it out by creating a sample project, SampleBehaviorLib. Basically, I was using the build and lib folders to create a NuGet package. (If you want to know more about the directory structure that has to be used to create NuGet packages, see here). The build folder contained 3 folders: x86, x64 & ARM each of which contained the respective binary. The lib folder contained the x86 version. The lib folder will allow the Visual Studio to work properly in Designer mode. The build folder also contained a .targets file which added the logic for selecting the appropriate binary based on the build platform.
Unfortunately, the above solution did not work for me. When I referenced the NuGet package (created using the above method) in another project, it built successfully for x86 platform. But for x64 and ARM version, the build failed. It gave the following error
1>C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v14.0\AppxPackage\Microsoft.AppXPackage.Targets(1151,5): error APPX1101:Payload contains two or more files with the same destination path 'SampleBehaviorLib.dll'. Source files: 
 1>C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v14.0\AppxPackage\Microsoft.AppXPackage.Targets(1151,5): error APPX1101: C:\Users\Guest01.nuget\packages\SampleBehaviorLib\1.0.8\lib\uap10.0\SampleBehaviorLib.dll
 1>C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v14.0\AppxPackage\Microsoft.AppXPackage.Targets(1151,5): error APPX1101: C:\Users\Guest01.nuget\packages\SampleBehaviorLib\1.0.8\build\uap10.0\x64\SampleBehaviorLib.dll
I visited the NuGet project in GitHub and raised this issue. The nice folks there helped me out. It seems I had to use the ref and runtimes folder instead of the lib and build folder. I revisited the NuGet 3 Package Authoring Document and I found out the following:
Ref folder: ref is a new, optional, directory that contains .NET assemblies defining the public surface (public types and methods) for an application to compile against. The assemblies in this folder may have no implementation, they are purely used to define surface area for the compiler. If the package has no ref directory, then the lib is both the reference assembly and the implementation assembly. It is useful for packages that need to provide a consistent surface area for compilation and intellisense but then have different implementation for different TxMs. [A TxM is the name of a platform that a given asset in a package is supposed to work for. Logically these are an extension of the Target Framework Monikers (TFM) e.g. net45, net46, netcore50, and dnxcore50 are all examples of TxMs. For UWP applications for Windows 10, the TxM  is uap10.0.]
Runtimes folder: runtimes is a new, optional, directory that will contain OS specific code, such as CPU architecture and OS specific or otherwise platform-dependent binaries. The runtimes folder contains assemblies and native libraries required to run on specific “runtimes”, which are generally defined by Operating System and CPU architecture. These runtimes are identified using Runtime Identifiers (RIDs) such as win, win-x86, win7-x86, win8-64, etc. For Windows 10 the RIDs are win10-x86, win10-x64 & win10-arm.
So, I have to compile the WPFSpark.UWP project for the three platforms – x86, x64 & ARM and put them in the appropriate folders within the runtimes folder. Next, I have to create an assembly (for WPFSpark.UWP project)which defines the surface area for the compiler and intellisense i.e. an assembly containing only the declarations of the methods and properties defined by the classes within WPFSpark.UWP project (but no implementation). Hmmm, seems like a tedious task. Fortunately, there is a NuGet package which does the job for you – Microsoft.DotNet.BuildTools.GenAPI. This is currently in beta. Therefore when you add the NuGet package (through Manage NuGet Packages option) make sure you check the Include prerelease option so that it is listed in the search results.
This is a very useful package but it does only half the job. According to the documentation of this package
This package provides build-time support generating reference assembly source. Referencing this package will cause the build to produce a CS file in the output directory that represents the public surface area of the project. This package requires .NET Framework 4.6 or later.
I raised an issue in this package’s GitHub forum¬†to know if there was an easy way to automate the creation of an assembly through a script. It seems there isn’t. I got help from the folks at the NuGet project. They said:
“You can create a project with similar characteristics as your implementation dll and include the source. We didn’t try to create a dll since everyone has a different build system with respect to versioning, signing, etc so we just emit a CS that you can plug into your own project.”
Ok, so now I create a Universal Class Library project in Visual Studio, name it WPFSpark.UWP.Ref and add the generated CS file to it. I build it in Release Mode for the AnyCPU platform. The resulting DLL file will be placed in the reference folder.
So now the WPFSpark.UWP project folder structure looks like this


The WPFSpark.UWP folder contains the source code for the controls. The WPFSpark.Ref folder contains the reference project created for the generated CS file and the WPFSparkUWP.Client folder contains a demo app which references the WPFSpark.UWP NuGet package. The NuGet folder contains the ref and runtimes folder along with WPFSpark.UWP.nuspec file. The contents of NuGet folder are used to create the WPFSpark.UWP NuGet package.
The NuGet folder structure looks like this
In order to keep the contents of the runtimes folder up-to-date, I have added the following script in the Build Action of the WPFSpark.UWP project
@echo Copying '$(TargetFileName)' to $(ProjectDir)..\Nuget\runtimes\win10-$(PlatformName)\lib\uap10.0
copy /Y $(TargetPath) $(ProjectDir)..\Nuget\runtimes\win10-$(PlatformName)\lib\uap10.0
This will ensure that whenever a build is made for a particular platform (x86/x64/ARM) the output DLL will be copied to the appropriate location in the runtimes folder.
A similar script has been added to the Build Action of the WPFSpark.UWP.Ref project in order to copy the output DLL to the reference folder.
@echo Copying '$(TargetFileName)' to $(ProjectDir)..\Nuget\ref\uap10.0
copy /Y $(TargetPath) $(ProjectDir)..\Nuget\ref\uap10.0
The CreateNuGetPackage.cmd contains the script for creating the WPFSpark.UWP NuGet package.
Hope this helps you create your own NuGet packages containing x86, x84 and ARM binaries.
Happy Coding!ūüôā