Maxime FRAPPAT

Hum …no thanks ! – Lordinaire

Tag: UWP (Page 2 of 2)

[UWP] Advanced ListView/GridView with animations and Win2D effects

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

[UWP] Adaptative streaming protocols

If you have ever tried to play with HLS (HTTP Live Streaming) with Windows Phone 8, you probably used the Player Framework with a MediaStreamSource (like Windows Phone Streaming Media).

With the new Windows 10 APIs, all that stuff become useless ! The MediaElement control of Universal Windows Platform app can handle that natively :) HLS or DASH (Dynamic Adaptive Streaming over HTTP) it’s up to you and it is very easy to use :

MediaElement mediaElement = new MediaElement();

// The source is an uri like :
// http://mydomain.com/toto/playlist.m3u8
var result = await AdaptiveMediaSource.CreateFromUriAsync(source);
if (result.Status == AdaptiveMediaSourceCreationStatus.Success)
{
    var astream = result.MediaSource;
    mediaElement.SetMediaStreamSource(astream);
}

Have fun :D

Page 2 of 2

Powered by WordPress & Theme by Anders Norén

%d bloggers like this: