CompositionProToolkit v0.4.1 released

Compared to the large number of features in CompositionProToolkit v0.4, version 0.4.1 incorporates the CompositionImageFrame control and code optimizations.

If you want to know more about the new features added to CompositionProToolkit v0.4 check my previous post.

CompositionImageFrame

CompositionImageFrame is a control which can be used for displaying images. It encapsulates a CompositionSurfaceImage object which is used for loading and rendering the images. It also supports Pointer interactions and raises events accordingly.

In order to configure the rendering of the image, CompositionImageFrame has the following properties

Dependency Property Type Description Default Value
AlignX AlignmentX Specifies how the image is positioned horizontally in the CompositionImageFrame. AlignmentX.Center
AlignY AlignmentY Specifies how the image is positioned vertically in the CompositionImageFrame. AlignmentY.Center
FrameBackground Color Specifies the background color of the CompositionImageFrame to fill the area where image is not rendered. Colors.Black
Interpolation CanvasImageInterpolation Specifies the interpolation used for rendering the image. HighQualityCubic
Source Uri Specifies the Uri of the image to be loaded into the CompositionImageFrame. null
Stretch Stretch Specifies how the image is resized to fill its allocated space in the CompositionImageFrame. Uniform

CompositionImageFrame raises the following events

  • ImageOpened – when the image has been successfully loaded from the Uri and rendered.
  • ImageFailed – when there is an error loading the image from the Uri.

CompositionProToolkit v0.4.1 is now available in NuGet. Source code is available in GitHub.

If you want to see the various controls and features of CompositionProToolkit in action, check out the SampleGallery project in GitHub.

CompositionProToolkit v0.4 Released!

I am thrilled to announce that CompositionProToolkit v0.4 is now released. It brings a few breaking changes and new features that will help you use the Composition APIs more efficiently. In this blog I will be talking in detail about them.

Breaking Changes

First, let me tell you about the breaking changes incorporated in CompositionProToolkit v0.4

  • ICompositionMaskGenerator has been renamed to ICompositionGenerator – you will now be using this interface to generate many other objects apart from ICompositionMask. (More on that later!)
  • ICompositionMaskGeneratorInternal has been renamed to ICompositionGeneratorInternal – this will not break any of your code as it is an internal class.
  • CompositionMaskGenerator has been renamed to CompositionGenerator – another internal class which implements the ICompositionGenerator and the ICompositionGeneratorInternal interfaces.
  • CompositionMaskFactory has been renamed to CompositionGeneratorFactory – You will be using this class to obtain the instance of ICompositionGenerator.
    • GetCompositionMaskGenerator API has been renamed to GetCompositionGenerator – The parameters of this API remain the same.
public static ICompositionGenerator GetCompositionGenerator(Compositor compositor,     
    CompositionGraphicsDevice graphicsDevice = null, object sharedLock = null)

Now that we are done with the breaking changes, lets discuss the new features that have been added to CompositionProToolkit.

Creating a ICompositionMask using ICanvasBrush

ICompositionGenerator now provides the following additional API to create a ICompositionMask using an ICanvasBrush or its derivatives like CanvasImageBrush, CanvasLinearGradientBrush, CanvasRadialGradientBrush and CanvasSolidColorBrush.

Task<ICompositionMask> CreateMaskAsync(Size size, CanvasGeometry geometry, ICanvasBrush brush);

ICompositionSurfaceImage

ICompositionSurfaceImage is an interface which encapsulates a CompositionDrawingSurface onto which an image can be loaded by providing a Uri. You can then use the CompositionDrawingSurface to create a CompositionSurfaceBrush which can be applied to any Visual.

SurfaceImage.png

ICompositionGenerator provides the following API which allows you to create a CompositionSurfaceImage

Task<ICompositionSurfaceImage> CreateSurfaceImageAsync(Uri uri, Size size, 
    CompositionSurfaceImageOptions options);

This API requires the Uri of the image to be loaded, the size of the CompositionDrawingSurface (usually the same size as the Visual on which it is finally applied) and the CompositionSurfaceImageOptions.

CompositionSurfaceImageOptions

The CompositionSurfaceImageOptions class encapsulates a set of properties which influence the rendering of the image on the CompositionSurfaceImage. The following table shows the list of these properties.

Property Type Description Possible Values
Stretch Stretch Describes how image is resized to fill its allocated space. None,
Uniform,
Fill,
UniformToFill
HorizontalAlignment AlignmentX Describes how image is positioned horizontally. Left,
Center,
Right
VerticalAlignment AlignmentY Describes how image is positioned vertically. Top,
Center,
Bottom
Opacity float Specifies the opacity of the rendered image. 0 - 1f inclusive
Interpolation CanvasImageInterpolation Specifies the interpolation used to render the image. NearestNeighbor, Linear,
Cubic,
MultiSampleLinear,
Anisotropic,
HighQualityCubic
SurfaceBackgroundColor Color Specifies the color which will be used to fill the surface before rendering the image. All possible values that can be created.

Here is how the image is aligned on the Visual’s surface based on the HorizontalAlignment and VerticalAlignment properties

Alignment

The area on the Visual’s surface, which is not covered by the rendered image, will be filled with the color specified by the SurfaceBackgroundColor property

Example

The following example shows how you can load an image onto a Visual

var compositor = ElementCompositionPreview.GetElementVisual(this).Compositor;
var generator = CompositionGeneratorFactory.GetCompositionGenerator(compositor);

var visual = compositor.CreateSpriteVisual();
visual.Size = new Vector2(this.ActualWidth.Single(), this.ActualHeight.Single());
visual.Offset = Vector3.Zero;

var options = new CompositionSurfaceImageOptions(Stretch.Uniform, AlignmentX.Center,
    AlignmentY.Center, 0.9f, CanvasImageInterpolation.Cubic)
    {
        SurfaceBackgroundColor = Colors.Maroon
    };

var surfaceImage =
    await generator.CreateSurfaceImageAsync(new Uri("ms-appx:///Assets/Images/Image3.jpg"), 
          visual.Size.ToSize(), options);

visual.Brush = compositor.CreateSurfaceBrush(_surfaceImage.Surface);

Once you create a CompositionSurfaceBrush from the ICompositionSurfaceImage and apply it to a Visual, you can use the following ICompositionSurfaceImage APIs to resize, provide a new Uri or change the rendering options

Task RedrawAsync();

Task RedrawAsync(CompositionSurfaceImageOptions options);

Task RedrawAsync(Uri uri, CompositionSurfaceImageOptions options);

Task RedrawAsync(Uri uri, Size size, CompositionSurfaceImageOptions options);

Task ResizeAsync(Size size);

Task ResizeAsync(Size size, CompositionSurfaceImageOptions options);

Once you call any of the above methods, the Visual’s brush is also updated.

Visual Reflection

A new API has been added to CompositionProToolkit which allows you to create the reflection of any Visual.

Task CreateReflectionAsync(ContainerVisual visual, float reflectionDistance = 0f,
    float reflectionLength = 0.7f, ReflectionLocation location = ReflectionLocation.Bottom);

The parameters required for this API are

  • visual – A ContainerVisual whose reflection has to be created.
  • reflectionDistance – The distance between the visual and its reflection.
  • reflectionLength – The normalized length (0 – 1f, inclusive) of the visual that must be visible in the reflection. Default value is 0.7f.
  • location – Specifies the location of the reflection with respect to the visual – Bottom, Top, Left or Right. Default value is ReflectionLocation.Bottom.

ReflectionLocation.png

This API will create a reflection even if an effect is applied to the Visual.

ReflectionWithEffect.png

If the visual has multiple other visuals in its visual tree, then the entire visual tree is reflected.

ReflectionMultipleVisuals.png

Other Changes

With the Anniversary Release of Windows 10, CompositionProToolkit project has migrated to Windows SDK 14393. Also it now requires CompositionExpressionToolkit v0.2.4 or higher, and Win2d v1.19 or higher (this is added automatically when you add a NuGet reference to CompositionProToolkit).

The source code is available in GitHub and the NuGet library is available here.

Creating a FluidBanner control using Windows Composition

Introduction

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.

fb3.gif

 

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.

InsetClip.PNG

 

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).

FluidBanner_Layout

  • 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_Selected

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.

LayoutPass2

 

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
        InitializeComposition();
    }

    ...
}

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.

BannerItem

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.

BannerItem

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.

FB_LayoutAnim

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

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.

FluidProgressRing.gif

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.

CompositionExpressionToolkit

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

cet_banner

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.

DynamicFWP

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;
                break;
            case PageBase.PageDisplayType.Display4x3:
                rows = 4;
                columns = 3;
                break;
            case PageBase.PageDisplayType.Display6x2:
                rows = 6;
                columns = 2;
                break;
            default:
                throw new ArgumentOutOfRangeException(nameof(newPageDisplay), newPageDisplay, null);
        }

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

    #endregion

    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"
                xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                xmlns:local="using:TestFWPanel"
                xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
                xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
                xmlns:wpfSpark="using:WPFSpark"
                x:Name="RootPage"
                mc:Ignorable="d">
    <local:PageBase.Resources>
        <x:Double x:Key="SmallScreenWidth">0</x:Double>
        <x:Double x:Key="MediumScreenWidth">540</x:Double>
        <x:Double x:Key="LargeScreenWidth">900</x:Double>
    </local:PageBase.Resources>
    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <VisualStateManager.VisualStateGroups>
            <VisualStateGroup>
                <VisualState x:Name="Small">
                    <VisualState.StateTriggers>
                        <AdaptiveTrigger MinWindowWidth="{StaticResource SmallScreenWidth}" />
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                        <Setter Target="RootPage.PageDisplay"
                                Value="Display6x2"></Setter>
                    </VisualState.Setters>
                </VisualState>
                <VisualState x:Name="Medium">
                    <VisualState.StateTriggers>
                        <AdaptiveTrigger MinWindowWidth="{StaticResource MediumScreenWidth}" />
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                        <Setter Target="RootPage.PageDisplay"
                                Value="Display4x3" />
                    </VisualState.Setters>
                </VisualState>
                <VisualState x:Name="Large">
                    <VisualState.StateTriggers>
                        <AdaptiveTrigger MinWindowWidth="{StaticResource LargeScreenWidth}" />
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                        <Setter Target="RootPage.PageDisplay"
                                Value="Display3x4" />
                    </VisualState.Setters>
                </VisualState>
            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>
        <Grid x:Name="ContainerGrid"
              HorizontalAlignment="Stretch"
              VerticalAlignment="Stretch">
            <wpfSpark:FluidWrapPanel x:Name="fwPanel"
                                     Background="Beige"
                                     Margin="0"
                                     HorizontalAlignment="Stretch"
                                     VerticalAlignment="Stretch"
                                     ItemWidth="2"
                                     ItemHeight="2"
                                     IsComposing="True"
                                     SizeChanged="OnFWPSizeChanged"></wpfSpark:FluidWrapPanel>
        </Grid>
    </Grid>
</local:PageBase>

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()))
        return;

    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.