Today, I wanted to mix some features like AdaptiveTrigger and Win2D in a UWP project. The result is pretty convincing (for me) !

Basics

I will focus on the XAML because that is not an example for teaching you how to use MVVM and stuff like that. So, my page looks like that :

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition />
        </Grid.RowDefinitions>

        <TextBlock Text="SIMPSON FAMILY" FontSize="48" Margin="10" />

        <GridView Grid.Row="1" Margin="10,0" ItemsSource="{Binding Users}" ItemTemplate="{StaticResource UserDataTemplate}">
        </GridView>
    </Grid>

and the DataTemplate :

<DataTemplate x:Key="UserDataTemplate">
    <Grid BorderBrush="#FF222222" BorderThickness="7" Height="200" Width="450">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>

        <Image Stretch="Uniform" VerticalAlignment="Stretch" HorizontalAlignment="Center" Source="{Binding ProfileImage}" />

        <Border Background="#FF222222" Grid.Column="1">
            <StackPanel Orientation="Vertical" Margin="5">
                <TextBlock Margin="10,0" Text="{Binding Name}" Foreground="HotPink" FontWeight="Bold" VerticalAlignment="Center" TextWrapping="WrapWholeWords" />

                <TextBlock Margin="10,0" Foreground="HotPink" Text="{Binding Description}" VerticalAlignment="Center" TextWrapping="WrapWholeWords" TextTrimming="WordEllipsis" />
            </StackPanel>
        </Border>
    </Grid>                    
</DataTemplate>

wideContent

Help me AdaptiveTrigger !

In an Universal Windows Platform application, we can (must?) adapt the content when the window size is changing. To achieve that, we need to use the AdaptiveTrigger inside a VisualStateManager to change the layout when some conditions are done.

In our example, the layout will change when the width will be under 720px.

<DataTemplate x:Key="UserDataTemplate">
    <UserControl>
        <Grid>
            <Grid x:Name="WideContent" x:DeferLoadStrategy="Lazy" Visibility="Collapsed" BorderBrush="#FF222222" BorderThickness="7" Height="200" Width="450">
                <Grid.RenderTransform>
                    <CompositeTransform />
                </Grid.RenderTransform>

                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto" />
                    <ColumnDefinition />
                </Grid.ColumnDefinitions>

                <Image Stretch="Uniform" VerticalAlignment="Stretch" HorizontalAlignment="Center" Source="{Binding ProfileImage}" />

                <Border Background="#FF222222" Grid.Column="1">
                    <StackPanel Orientation="Vertical" Margin="5">
                        <TextBlock Margin="10,0" Text="{Binding Name}" Foreground="HotPink" FontWeight="Bold" VerticalAlignment="Center" TextWrapping="WrapWholeWords" />

                        <TextBlock Margin="10,0" Foreground="HotPink" Text="{Binding Description}" VerticalAlignment="Center" TextWrapping="WrapWholeWords" TextTrimming="WordEllipsis" />
                    </StackPanel>
                </Border>
            </Grid>

            <Grid x:Name="SmallContent" x:DeferLoadStrategy="Lazy" Visibility="Collapsed">
                <Grid.RowDefinitions>
                    <RowDefinition />
                    <RowDefinition Height="Auto" />
                </Grid.RowDefinitions>

                <Border Grid.Row="1" Margin="0,-10,0,0" Background="#FF222222" />

                <TextBlock Grid.Row="1" Margin="10,5" Text="{Binding Name}" Foreground="HotPink" FontWeight="Bold" VerticalAlignment="Bottom" HorizontalAlignment="Center" TextWrapping="WrapWholeWords" RenderTransformOrigin="0.5,0.5" />
                            
                <Image Stretch="Uniform" Width="100" Height="100" VerticalAlignment="Center" HorizontalAlignment="Center" Source="{Binding ProfileImage}" />                        
            </Grid>

            <VisualStateManager.VisualStateGroups>
                <VisualStateGroup>
                    <VisualState x:Name="Small">
                        <VisualState.StateTriggers>
                            <AdaptiveTrigger MinWindowWidth="0" />
                        </VisualState.StateTriggers>
                        <VisualState.Setters>
                            <Setter Target="SmallContent.Visibility" Value="Visible" />
                        </VisualState.Setters>                                
                    </VisualState>
                    <VisualState x:Name="Wide">
                        <VisualState.StateTriggers>
                            <AdaptiveTrigger MinWindowWidth="720" />
                        </VisualState.StateTriggers>
                        <VisualState.Setters>
                            <Setter Target="WideContent.Visibility" Value="Visible" />
                        </VisualState.Setters>
                    </VisualState>
                </VisualStateGroup>
            </VisualStateManager.VisualStateGroups>
        </Grid>
    </UserControl>
</DataTemplate>

And with animations ?

In a VisualState, you can add some animations that will be played when the state will applied.

<VisualState x:Name="Wide">
    <VisualState.StateTriggers>
        <AdaptiveTrigger MinWindowWidth="720" />
    </VisualState.StateTriggers>
    <VisualState.Storyboard>
        <Storyboard>
            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="WideContent">
                <EasingDoubleKeyFrame KeyTime="0" Value="0" />
                <EasingDoubleKeyFrame KeyTime="0:0:0.3" Value="1" />
            </DoubleAnimationUsingKeyFrames>
            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.TranslateX)" Storyboard.TargetName="WideContent">
                <EasingDoubleKeyFrame KeyTime="0:0:0.0" Value="-50" />
                <EasingDoubleKeyFrame KeyTime="0:0:0.15" Value="0" />
            </DoubleAnimationUsingKeyFrames>
        </Storyboard>
    </VisualState.Storyboard>
    <VisualState.Setters>
        <Setter Target="WideContent.Visibility" Value="Visible" />
    </VisualState.Setters>
</VisualState>

Bonus : Some effects with Win2D

The two visual states (Small and Wide) works fine but it’s not very convincing. What if we add some effects on the image with Win2D to create a thumbnail-style ?

First, install Win2D packages from Nuget :

Install-Package Win2D.uwp

The main idea is to create a custom control who takes in parameter the image to convert into a thumbnail. Effects are applied to the image then we use an ImageBrush to display the new image in an Ellipse.

Edit: Control updated due to Shawn’s reply

using System;
using Windows.ApplicationModel;
using Windows.UI;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Shapes;
using Microsoft.Graphics.Canvas;
using Microsoft.Graphics.Canvas.Effects;
using Microsoft.Graphics.Canvas.UI.Xaml;

namespace DynamicListView
{
    public class ImageEffect : UserControl
    {
        #region Dependency Properties

        #region BlurImage

        public static readonly DependencyProperty BlurImageProperty = DependencyProperty.Register(
            "BlurImage", typeof(string), typeof(ImageEffect), new PropertyMetadata(default(string), OnBlurImageChanged));

        private static void OnBlurImageChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
        {
            var dynamicEffectControl = sender as ImageEffect;
            dynamicEffectControl?.Initialize();
        }

        public string BlurImage
        {
            get { return (string)GetValue(BlurImageProperty); }
            set { SetValue(BlurImageProperty, value); }
        }

        #endregion

        #endregion

        private async void Initialize()
        {
            if (DesignMode.DesignModeEnabled)
                return;

            var imageBrush = new ImageBrush();
            var device = new CanvasDevice();
            var bitmap = await CanvasBitmap.LoadAsync(device, new Uri(BlurImage));
            var renderer = new CanvasImageSource(device, bitmap.SizeInPixels.Width, bitmap.SizeInPixels.Height,
                bitmap.Dpi);

            imageBrush.ImageSource = renderer;

            using (var ds = renderer.CreateDrawingSession(Colors.Transparent))
            {
                var grayscale = new GrayscaleEffect();
                var blur = new GaussianBlurEffect();

                grayscale.Source = bitmap;

                blur.BlurAmount = 10f;
                blur.BorderMode = EffectBorderMode.Soft;
                blur.Optimization = EffectOptimization.Balanced;
                blur.Source = grayscale;

                ds.DrawImage(blur);
            }

            Ellipse ellipse = new Ellipse
            {
                Width = 100,
                Height = 100,
                Stroke = new SolidColorBrush(Color.FromArgb(0xFF, 0x22, 0x22, 0x22)),
                StrokeThickness = 7
            };
            ellipse.VerticalAlignment = VerticalAlignment.Center;
            ellipse.HorizontalAlignment = HorizontalAlignment.Center;
            ellipse.Fill = imageBrush;

            Content = ellipse;
        }
    }
}

Ok, I’m not a designer but it’s pretty cool no ?

Animation