Visually Located

XAML and GIS

Easily Create Parallax effects in Windows 10 apps

In October James Clarke tweeted some source code to add a parallax effect to UWP apps using the new Windows Composition framework.

At the time the new SDK was not available so you could not get it working. Now that the new SDK is released we can start building apps using Windows Composition. The sample that James posted is nice and short, but it’s still code that you would need to repeat over and over. As I’ve mentioned before, I’m not a fan of repeating code. I am however a fan of custom controls and behaviors. This effect can easily be made into a behavior.

In James’ sample he is parallaxing an image, but there is no reason it has to be an image. It could be any visual element. This allows you to use graphics or whatever you want as a background.

public class ParallaxBehavior : Behavior<FrameworkElement>
{
    /// <summary>
    /// Gets or sets the element that will parallax while scrolling.
    /// </summary>
    public UIElement ParallaxContent
    {
        get { return (UIElement)GetValue(ParallaxContentProperty); }
        set { SetValue(ParallaxContentProperty, value); }
    }
 
    public static readonly DependencyProperty ParallaxContentProperty = DependencyProperty.Register(
        "ParallaxContent", 
        typeof(UIElement), 
        typeof(ParallaxBehavior), 
        new PropertyMetadata(null, OnParallaxContentChanged));
 
    /// <summary>
    /// Gets or sets the rate at which the ParallaxContent parallaxes.
    /// </summary>
    public double ParallaxMultiplier
    {
        get { return (double)GetValue(ParallaxMultiplierProperty); }
        set { SetValue(ParallaxMultiplierProperty, value); }
    }
 
    public static readonly DependencyProperty ParallaxMultiplierProperty = DependencyProperty.Register(
        "ParallaxMultiplier", 
        typeof(double), 
        typeof(ParallaxBehavior), 
        new PropertyMetadata(0.3d));
 
    protected override void OnAttached()
    {
        base.OnAttached();
        AssignParallax();
    }
 
    private void AssignParallax()
    {
        if (ParallaxContent == null) return;
        if (AssociatedObject == null) return;
 
        var scroller = AssociatedObject as ScrollViewer;
        if (scroller == null)
        {
            scroller = AssociatedObject.GetChildOfType<ScrollViewer>();
        }
        if (scroller == null) return;
 
        CompositionPropertySet scrollerViewerManipulation = ElementCompositionPreview.GetScrollViewerManipulationPropertySet(scroller);
 
        Compositor compositor = scrollerViewerManipulation.Compositor;
 
        ExpressionAnimation expression = compositor.CreateExpressionAnimation("ScrollManipululation.Translation.Y * ParallaxMultiplier");
 
        expression.SetScalarParameter("ParallaxMultiplier", (float)ParallaxMultiplier);
        expression.SetReferenceParameter("ScrollManipululation", scrollerViewerManipulation);
 
        Visual textVisual = ElementCompositionPreview.GetElementVisual(ParallaxContent);
        textVisual.StartAnimation("Offset.Y", expression);
    }
 
    private static void OnParallaxContentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var b = d as ParallaxBehavior;
        b.AssignParallax();
    }
 }

You will need to reference the new UWP Behaviors SDK from nuget for this behavior

Now adding the parallax effect to your pages is very simple.

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <Image x:Name="ParallaxImage" Source="ms-appx:///Assets/Guadeloupe.jpg" Stretch="Fill"/>
    <ScrollViewer>
        <TextBlock HorizontalAlignment="Stretch" TextWrapping="WrapWholeWords" FontSize="30" Foreground="Black">
                Lorem ipsum dolor sit amet, consectetur adipiscing elit. 
                Nunc fringilla ultrices est eu ornare. 
                Suspendisse purus massa, iaculis in finibus dictum, gravida vel purus. 
                Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos.
        </TextBlock>
        <interactivity:Interaction.Behaviors>
            <behaviors:ParallaxBehavior ParallaxContent="{Binding ElementName=ParallaxImage}" />
        </interactivity:Interaction.Behaviors>    
    </ScrollViewer>                                
</Grid>

I took the code from James’ sample, but shortened the text of the TextBlock for easier viewing.

This behavior can also be used when you have controls that have ScrollViewers within them like the ListView

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <ListView>
        <ListView.Header>
            <Image x:Name="ParallaxImage" Source="ms-appx:///Assets/Guadeloupe.jpg" Stretch="UniformToFill"/>
        </ListView.Header>
        <ListView.ItemContainerStyle>
            <Style TargetType="ListViewItem">
                <Setter Property="Background" Value="White"/>
            </Style>
        </ListView.ItemContainerStyle>
        <x:String>Lorem ipsum dolor sit amet, consectetur adipiscing.</x:String>
        <x:String>Nunc fringilla ultrices est eu ornare.</x:String>
        <x:String>Suspendisse purus massa, iaculis in finibus dictum, gravida vel purus.</x:String>
        <interactivity:Interaction.Behaviors>
            <behaviors:ParallaxBehavior ParallaxContent="{Binding ElementName=ParallaxImage}" ParallaxMultiplier="-0.2"/>
        </interactivity:Interaction.Behaviors>
    </ListView>
</Grid>

You can find the source for the behavior and three samples on GitHub.

And here is a video demonstrating three samples.

Creating C# classes from JSON (or XML) in Visual Studio 2015

I am continually creating classes from JSON. In the past I have used json2csharp.com to accomplish this task. It is a very simply website with one large textbox. Paste your text or a link to text and click the generate button and suddenly you have C# classes!

json2csharp

Visual Studio 2015 removes the need for this website. The Visual Studio team has added a new way to paste JSON or XML text and have classes created. This new feature even works better than json2charp.com did. To use the new feature follow the below steps

  1. Open a file. If you want to paste JSON the file must be a class file (have the .cs extension).
  2. Copy some JSON
  3. Select Edit –> Paste Special –> Paste JSON As Classes
    paste-menu

It will generate as many classes as are needed. I’ll give a few examples to see the benefits of this new feature and how it could be improved.

Simple base types

{
    "firstName" : "John",
    "lastName" : "Doe",
    "age" : 13,
    "dateOfBirth" : "2002-10-05T14:13:25-06:00"
}

And the generated class

public class Rootobject
{
    public string firstName { get; set; }
    public string lastName { get; set; }
    public int age { get; set; }
    public DateTime dateOfBirth { get; set; }
}

You’ll notice that the properties are in lower camel case to match the property names of the JSON. Notice that the age property is correctly of type int and that the dateOfBirth property is of type DateTime. json2csharp.com would turn this property into a string. However, the dateOfBirth property in JSON format included an offset from UTC so the property should have been of type DateTimeOffset to ensure that it is properly de-serialized.

Multiple classes

What if you have multiple classes that need to be de-serialized? Each class will be generated.

{
    "id" : 12345,
    "friend" : {
        "name" : "Mickey"
    }
}
public class Rootobject
{
    public int id { get; set; }
    public Friend friend { get; set; }
}
 
public class Friend
{
    public string name { get; set; }
}

Collections

Collections of items are handled as well, but not in a very great way. If you change the friend property to be a collection of friends, we get the following

public class Rootobject
{
    public int id { get; set; }
    public Friend[] friends { get; set; }
}
 
public class Friend
{
    public string name { get; set; }
}

Notice that it made a Friend array rather than using the preferred (in my opinion) IEnumerable<Friend>. This is primarily because some JSON converters need a concrete class to de-serialize to. Serializers like JSON.Net allow you to specify an interface and it will default to using a concrete class. So if you specify IEnumerable<T> JSON.Net will create a new List<T>.

Error handling

If the JSON you are pasting contains errors, Visual Studio will tell you the line and position of the error! Maybe you wrote the JSON incorrectly or copied only part of it. The following example is missing a comma.:

{
    "id" : 12345
    "name" : "Shawn"
}

The following error message is shown:

error

Room for improvement

One of the biggest problems with json2csharp.com is it’s ability to “reuse” a class. This problem is present within the new Visual Studio feature as well. Take the following example of providing paging links in a result.

{
    "items" : [
        {
            "id" : 11,
            "id" : 12
        }
    ],
    "previous":{
        "href": "http://paging.com/1"
    },
    "next":{
        "href": "http://paging.com/3"
    }
}

The resulting class generation is to create a class for “previous” and for “next”.

public class Rootobject
{
    public Item[] items { get; set; }
    public Previous previous { get; set; }
    public Next next { get; set; }
}
 
public class Previous
{
    public string href { get; set; }
}
 
public class Next
{
    public string href { get; set; }
}
 
public class Item
{
    public int id { get; set; }
}

It would be great if it would “reuse” the first class it created rather than creating a new one. Deleting the extra classes can become quote a pain when you have some json that returns say 10 or more links to other information as well. Side note: If anyone knows if I can help contribute to this tool please let me know!

Another area for improvement is to say you want to default to using a particular JSON serializer so that it could capitalize property names. For example, if I could specify that I wanted to use JSON.Net then it would capitalize the classes and add the JsonPropertyAttribute for proper serializing and de-serializing.

Customizing the “selected” HubSection Header

I saw a question today about changing the foreground color of the “visible” or “selected” HubSection. Someone had pointed out that I have a behavior for getting the selected index of a Hub and it could be combined with a converter to give the desired output. I thought the solution to this problem was actually quiet simpler. If we can change the visual state of the hub sections when they change, we can give them a “selected” or “unselected” state. For this we can listen to the SectionsIsViewChanged event

public class CustomHub : Hub
{
    HubSection _lastSelectedSection = null;
 
    public CustomHub()
    {
        SectionsInViewChanged += OnSectionsInViewChanged;
    }
 
    private void OnSectionsInViewChanged(object sender, SectionsInViewChangedEventArgs sectionsInViewChangedEventArgs)
    {
        if (_lastSelectedSection == SectionsInView[0]) return;
        
        VisualStateManager.GoToState(SectionsInView[0], "Selected", true);
        if (_lastSelectedSection != null)
        {
            VisualStateManager.GoToState(_lastSelectedSection, "Unselected", true);                
        }
        _lastSelectedSection = SectionsInView[0];
    }
}

NOTE: This sample does not set all sections to the “Unselected” state to begin with. For this sample, the state is blank so it doesn’t offer anything.

Now it’s just a matter of modifying the style of the HubSection to have these states.

Use the following style for the section

<Style TargetType="HubSection">
    <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
    <Setter Property="VerticalContentAlignment" Value="Stretch"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="HubSection">
                <Grid Background="{TemplateBinding Background}" Margin="{StaticResource HubSectionMarginThickness}">
                    <Grid.RowDefinitions>
                        <RowDefinition Height="auto"/>
                        <RowDefinition Height="auto"/>
                        <RowDefinition Height="*"/>
                    </Grid.RowDefinitions>
                    <VisualStateManager.VisualStateGroups>
                        <VisualStateGroup x:Name="SelectionStates">
                            <VisualState x:Name="Selected">
                                <Storyboard>
                                    <ColorAnimation Duration="0" To="#FFF30707" Storyboard.TargetProperty="(Control.Foreground).(SolidColorBrush.Color)" Storyboard.TargetName="Header" d:IsOptimized="True"/>
                                </Storyboard>
                            </VisualState>
                            <VisualState x:Name="Unselected"/>
                        </VisualStateGroup>
                    </VisualStateManager.VisualStateGroups>                            
                    <Grid.RenderTransform>
                        <CompositeTransform x:Name="WrappingTransform"/>
                    </Grid.RenderTransform>
                    <Rectangle x:Name="HubHeaderPlaceholder" Grid.Row="0"/>
                    <ContentControl x:Name="Header" CharacterSpacing="{StaticResource HubSectionHeaderCharacterSpacing}" ContentTemplate="{TemplateBinding HeaderTemplate}" Content="{TemplateBinding Header}" FontWeight="SemiBold" FontSize="{StaticResource HubSectionHeaderFontSize}" FontFamily="{StaticResource PhoneFontFamilyNormal}" HorizontalAlignment="Left" Margin="{StaticResource HubSectionHeaderMarginThickness}" Grid.Row="1">
                        <ContentControl.RenderTransform>
                            <CompositeTransform x:Name="StickyHeaderTransform"/>
                        </ContentControl.RenderTransform>
                    </ContentControl>
                    <ContentPresenter x:Name="ContentPresenter" ContentTemplate="{TemplateBinding ContentTemplate}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" Grid.Row="2" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Notice the VisualStateGroup with the “Selected” and “Unselected” states. You’ll want to add this style to your page, or app resources. Using this new way of styling does require the use of the new CustomHub control over the standard Hub control.

<controls:CustomHub Header="Hello world">
    <HubSection Header="one">
 
    </HubSection>
    <HubSection Header="two">
 
    </HubSection>
    <HubSection Header="three">
 
    </HubSection>
</controls:CustomHub>

What other ways can you think to customize the ”selected” HubSection?

Disable screen capture for your Universal Windows apps

If you are concerned about security within your apps, you will want to disable the ability to taker screenshots of your app. This was a feature introduced in Windows Phone 8 as well as Windows Store 8.1 apps. With universal apps, you can now disable screenshots with the same one line of code

Windows.UI.ViewManagement.ApplicationView.GetForCurrentView().IsScreenCaptureEnabled = false;

This allows the application itself to not allow screenshots. So if you are running on desktop and you take a screen shot, the application will show black.

Here is what it looks like on desktop with screen capture enabled

Enabled

Here is what it looks like on desktop with screen capture disabled

Disabled

I’m not able to get the phone emulator to take a physical screenshot (just the emulator screenshot) so I cannot show this. And until a new phone build comes out I cannot show it.

Setting the device wallpaper in Windows Universal Apps

Windows 10 launched today and with it comes a new SDK for building “Universal Apps”. This new SDK comes with a lot of new functionality. One of those pieces is a new way to set the wallpaper of a device. This new API is available on the new UserProfilePersonalizationSettings class. This class is used for setting the lockscreen, and the device wallpaper. The new UserProfilePersonalizationSettings class has a TrySetWallpaperImageAsync method that accepts any local StorageFile (I have not been able to set the wallpaper using the FileOpenPicker). This means you can save an image to the local or roaming folders, or use an existing image packaged with your app.

var file = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///Assets/AwesomeWallpaper.jpg"));
await Windows.System.UserProfile.UserProfilePersonalizationSettings.Current.TrySetWallpaperImageAsync(file);

This example uses a file packaged with the app in the Assets folder.

image

You should check if the device has the ability to change the wallpaper. I’m guessing this is in place for future Windows settings.

if (Windows.System.UserProfile.UserProfilePersonalizationSettings.IsSupported())
{
    var file = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///Assets/AwesomeWallpaper.jpg"));
    await UserProfilePersonalizationSettings.Current.TrySetWallpaperImageAsync(file);
}

Now go and create some amazing wallpaper apps for Windows 10

Setting the lockscreen in Windows Universal Apps

Windows 10 launched today and with it comes a new SDK for building “Universal Apps”. This new SDK comes with a lot of new functionality. One of those pieces is a new way to set the lockscreen of a device. This new API is available on the new UserProfilePersonalizationSettings class. Previously you only had the ability to set the lockscreen within phone apps using the SetImageUri method on the static LockScreen class. The method accepted a Uri that had to be local to the app.

Windows.Phone.System.UserProfile.LockScreen.SetImageUri(new Uri(localFileName));

The new UserProfilePersonalizationSettings class has a TrySetLockScreenImageAsync method that accepts any local StorageFile (I have not been able to set the lockscreen using the FileOpenPicker). This means you can save an image to the local or roaming folders, or use an existing image packaged with your app.

var file = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///Assets/AwesomeLockscreen.jpg"));
await Windows.System.UserProfile.UserProfilePersonalizationSettings.Current.TrySetLockScreenImageAsync(file);

This example uses a file packaged with the app in the Assets folder.

image

You should check if the device has the ability to change the lockscreen. I’m guessing this is in place for future Windows settings.

if (Windows.System.UserProfile.UserProfilePersonalizationSettings.IsSupported())
{
    var file = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///Assets/AwesomeLockscreen.jpg"));
    await UserProfilePersonalizationSettings.Current.TrySetLockScreenImageAsync(file);
}

Now go and create some amazing lockscreen apps for Windows 10

Persist ListView scroll position without setting NavigationCacheMode

In a previous post I wrote about why your ListView resets to the top of the list when navigating backwards. In that post I looked at using the NavigationCacheMode of the page to persist the scroll position of your ListView. I also briefly mentioned using the ScrollIntoView method of the ListView. In this post we’ll look at a little known helper class that allows you to keep your scroll position without using all the memory that NavigationCacheMode can use.

ListViewPersistenceHelper

The ListViewPersistenceHelper class allows you to easily restore the scroll position with only two methods, GetRelativeScrollPosition and SetRelativeScrollPositionAsync. These methods use the item that is at the top of the list as indicators for where the scroll position should be.

The GetRelativeScrollPosition should be called when navigating away from the page. A good place is the OnNavigatedFrom method.

protected override void OnNavigatedFrom(NavigationEventArgs e)
{
    _positionKey = ListViewPersistenceHelper.GetRelativeScrollPosition(myListView, ItemToKeyHandler);
    base.OnNavigatedFrom(e);
}
 
private string ItemToKeyHandler(object item)
{
    MyDataItem dataItem = item as MyDataItem;
    if (dataItem == null) return null;
 
    return dataItem.Id;
}

The SetRelativeScrollPositionAsync method tells the ListView where to scroll to. A good place to call this method is in the OnNavigatedTo method.

protected override void OnNavigatedTo(NavigationEventArgs e)
{
    base.OnNavigatedTo(e);
    if (_positionKey == null) return;
 
    ListViewPersistenceHelper.SetRelativeScrollPositionAsync(myListView, _positionKey, KeyToItemHandler);
}
 
private IAsyncOperation<object> KeyToItemHandler(string key)
{
    Func<System.Threading.CancellationToken, System.Threading.Tasks.Task<object>> taskProvider = token =>
    {
        var viewModel = DataContext as MyViewModel;
        if (viewModel == null) return null;
        foreach (var item in viewModel.Items)
        {
            if (item.Id == key) return Task.FromResult(item as object);
        }
        return Task.FromResult((object)null);
    };
 
    return AsyncInfo.Run(taskProvider);
}

In this simple example I’m storing the _positionKey key in a static field. This is ok for a simple case, but you may want to store this somewhere else.

Precautions:

I did notice that if you are animating the items of your ListView and using the ListViewPersistenceHelper, that there are some unintended side effects. The first few items of your ListView will still show the animation when navigating backward while the other items remain still. You can see this in the image below.

Animating side-effect

A simple work around for this is to reset the ItemContainerTransitions of the ListView in the OnNavigatedTo method if the _positionKey is not null.

protected override void OnNavigatedTo(NavigationEventArgs e)
{
    base.OnNavigatedTo(e);
    if (_positionKey == null) return;  
 
    myListView.ItemContainerTransitions = new TransitionCollection();
    ListViewPersistenceHelper.SetRelativeScrollPositionAsync(itemListView, _positionKey, KeyToItemHandler);
}

Hopefully this will help your apps use less memory while still maintaining a great experience for your users!

Easily create Microsoft Band Web Tiles with the Web Tiles Authoring website

The Microsoft Band team released “Web Tiles” today. Web Tiles are a simple way to show tiles on your Microsoft Band without needing to write an app. The tiles update from a web url. As far as I can tell, the web url must return json data. This data can be whatever but it needs to be open to everyone meaning no authentication. All of the articles today talk about the structure of a web tile and what each piece means. All this information is great, but if you only need a simple tile you can do without it.

Along with the release of Web Tiles, Microsoft also released a Web Tiles Authoring website. This website walks you through creating a new tile step by step. Let’s take a look at how this works

Step 1: Choose a layout

Here you pick which of the six layouts you’d like to use for the tile. You can choose from displaying a single page tile or multiple page tile. Multiple page tiles must link to an RSS or ATOM feed. This is not a requirement of web tiles; it is only a requirement for the authoring tool. Web tiles can have multiple pages without needing an ATOM or RSS feed.

Choose a layout

Step 2: Assign the url

Here you enter the url that will contain the data for your tile. If you picked a multiple page layout from Step 1, you must enter a URL for RSS or an ATOM feed. Again, this is not a requirement for web tiles, only for the authoring tool.

Assign the url

Step 3: Selecting information to display

Here you select what information from your json feed you would like to display. You simply drag the data from the right and drop it onto the place you’d like to display it.

Select information-a     Select information-b

Here we see that I selected to display the weather with the state being the header, the second line being the temp and the third line being some description.

Step 4: Make it yours

Here you’ll set the information to make the tile yours.

 Make it yours

Step 5: Download!

Download this awesome web tile to send to your favorite people

download

Hopefully the tool will be improved to allow for multiple pages when your resource is not an ATOM or RSS feed.

A simpler FilePicker for Windows Phone and Windows apps

If you’ve built a Windows Phone app that uses the FileOpenPicker you know that it can be a pain while the Windows variation is pretty simple to use. In Windows you use the PickSingleFileAsync method and continue code as you think you would.

StorageFile storageFile = await openPicker.PickSingleFileAsync();
// Do something with the file

However in Windows Phone this becomes a little more complex. It’s especially complex when you look at the sample on MSDN. First you call the PickSingleFileAndContinue method, then in App.xaml.cs wait for the app to be activated, then somehow get back to where you were before. It’s a mess. I wanted to make this easier in a recent app I was working on and I wanted it to work the same for Windows and Windows Phone.

To get started you’ll need to know that the CoreApplicationView can give us access to when the app is activated. With this, we can bypass all the weirdness of using the App class.

CoreApplication.GetCurrentView().Activated += OnViewActivated
...
...
private async void OnViewActivated(CoreApplicationView sender, IActivatedEventArgs args)
{
    sender.Activated -= OnViewActivated;
    // do some stuff
}

Let’s create a new Picker that will get any image files. We’ll create a new ImagePicker class. This class will be a shared class between Windows and Windows Phone apps. Let’s stub that out now.

public class ImagePicker
{
    public Task<IRandomAccessStream> PickAsync()
    {
        // TODO: this
    }
}

Pretty simple class, right? Let’s fill in the pieces. We’ll still use the FileOpenPicker within the PickAsync method. This is an ImagePicker so we want to state that it needs to look for images

public Task<IRandomAccessStream> PickAsync()
{
    FileOpenPicker openPicker = new FileOpenPicker();
    openPicker.ViewMode = PickerViewMode.Thumbnail;
    openPicker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;
    openPicker.FileTypeFilter.Add(".jpg");
    openPicker.FileTypeFilter.Add(".jpeg");
    openPicker.FileTypeFilter.Add(".png");
 
    // TODO: the rest
}

At this point we need to break up into Windows Phone code and Windows code

private TaskCompletionSource<IRandomAccessStream> _imageCompletionSource;
 
#if WINDOWS_PHONE_APP
public Task<IRandomAccessStream> PickAsync()
#else
public async Task<IRandomAccessStream> PickAsync()
#endif
{
    FileOpenPicker openPicker = new FileOpenPicker();
    openPicker.ViewMode = PickerViewMode.Thumbnail;
    openPicker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;
    openPicker.FileTypeFilter.Add(".jpg");
    openPicker.FileTypeFilter.Add(".jpeg");
    openPicker.FileTypeFilter.Add(".png");
 
#if WINDOWS_PHONE_APP
    _imageCompletionSource = new TaskCompletionSource<IRandomAccessStream>();
    CoreApplication.GetCurrentView().Activated += OnViewActivated;
    openPicker.PickSingleFileAndContinue();
    return _imageCompletionSource.Task;
#else
    StorageFile storageFile = await openPicker.PickSingleFileAsync();
    return await storageFile.OpenAsync(FileAccessMode.Read);
#endif
}

Notice for Windows Phone we will use a TaskCompletionSource to return the value. The result for the TaskCompletionSource will be set in the OnViewActivated method.

private async void OnViewActivated(CoreApplicationView sender, IActivatedEventArgs args)
{
    sender.Activated -= OnViewActivated;
    FileOpenPickerContinuationEventArgs pickerArgs = args as FileOpenPickerContinuationEventArgs;
 
    IRandomAccessStream stream = null;
    if (pickerArgs != null)
    {
        if (pickerArgs.Files.Count > 0)
        {
            StorageFile storageFile = pickerArgs.Files[0];
            stream = await storageFile.OpenAsync(FileAccessMode.Read);
        }
    }
    _imageCompletionSource.SetResult(stream);
}

Now with this simple class you can pick an image file like such

var picker = new ImagePicker();
var accessStream = await picker.PickAsync();

And that works the same for both Windows and Windows Phone. I did test this on a low memory device and it did work there as well!

You can find a “generic” version of this class here.

Creating a behavior or action to close a Flyout from a control within the Flyout

I recently started work on a new project and I’m trying hard to use “no code-behind”. This has been challenging for me as I tend to always put very view specific logic within the view itself. Things like navigation, starting storyboards or showing popups/flyouts. One thing I was trying to do recently was close a Flyout from a button within the flyout. My first approach was to try an EventTriggerBehavior for the Click event of the button along with the CallMethodAction. I tried two ways to call the Hide method on the flyout.

   1: <Button x:Name="AddButton" Content="Add Item" >
   2:     <Button.Flyout>
   3:         <Flyout x:Name="AddItemFlyout"
   4:                 Placement="Full">
   5:             <StackPanel>
   6:                 <TextBox x:Name="PlaceName" Header="Name"/>
   7:                 <Grid>
   8:                     <Grid.ColumnDefinitions>
   9:                         <ColumnDefinition/>
  10:                         <ColumnDefinition/>
  11:                     </Grid.ColumnDefinitions>
  12:                     <Button Content="Cancel" Margin="0,0,9.5,0" >
  13:                         <interactivity:Interaction.Behaviors>
  14:                             <core:EventTriggerBehavior EventName="Click">
  15:                                 <core:CallMethodAction TargetObject="{Binding ElementName=AddItemFlyout}"
  16:                                                        MethodName="Hide" />
  17:                             </core:EventTriggerBehavior>
  18:                         </interactivity:Interaction.Behaviors>
  19:                     </Button>
  20:                     <Button Content="Create" Grid.Column="1" Margin="9.5,0,0,0" 
  21:                             Command="{Binding CreateItemCommand}" 
  22:                             CommandParameter="{Binding Text, ElementName=PlaceName}">
  23:                         <interactivity:Interaction.Behaviors>
  24:                             <core:EventTriggerBehavior  EventName="Click">
  25:                                 <core:CallMethodAction TargetObject="{Binding Flyout, ElementName=AddButton}"
  26:                                                        MethodName="Hide" />
  27:                             </core:EventTriggerBehavior>
  28:                         </interactivity:Interaction.Behaviors>
  29:                     </Button>
  30:                 </Grid>
  31:             </StackPanel>
  32:         </Flyout>
  33:     </Button.Flyout>
  34: </Button>

Notice in the cancel button I tried hooking into the flyout by name and the create button I tried accessing the Flyout property of the button. Neither one of these would close the flyout. I searched online and found that this is not a new problem. Someone asked a question on StackOverflow a year ago.

My next step was to create a behavior and define a Flyout dependency property on it. When the button was clicked it would hide the flyout. This did not work because the Flyout property was always null. I tried setting it two different ways just as I had before.

With these not working I decided to walk the tree until I hit the Flyout and close it.

var flyout = AssociatedObject.GetVisualParent<Flyout>();
if(flyout != null)
{
    flyout.Hide();
}

NOTE: This uses an extension method for walking up the visual tree using the VisualTreeHelper.

This didn’t work because the Flyout is not an visual control. A Flyout is rendered with the FlyoutPresenter control. That’s all fine and dandy, but the FlyoutPresenter does not offer a way to close itself. So what to do? I went to XamlSpy to have a look at what is rendering the Flyout.

xamlspy

XamlSpy shows us that a popup is rendering the FlyoutPresenter. Perfect, we’ll walk the tree until we get to the popup, and then set IsOpen to false;

var popup = AssociatedObject.GetVisualParent<Popup>();
if(popup != null)
{
    popup.IsOpen = false;
}

Sweet! Let’s run this and watch the Flyout close! Click button… Wait… why isn’t it closing… Popup is null? Huh? It turns out that the VisualTreeHelper reports that the FlyoutPresenter does not have a parent. The VisualTreeHelper may report there isn’t a parent, but luckily the Parent property on the FlyoutPresenter does give us the Popup!

var flyout = AssociatedObject.GetVisualParent<FlyoutPresenter>();
if (flyout != null)
{
    var popup = flyout.Parent as Popup;
    if (popup != null)
    {
        popup.IsOpen = false;
    }
}

This allows the Flyout to close and even fires off the Closed event.

Here is some code to make this into a behavior that only works on buttons.

public class CloseFlyoutBehavior : Behavior<Button>
{
    protected override void OnAttached()
    {
        base.OnAttached();
        AssociatedObject.Click += AssociatedObjectOnClick;
    }
    private void AssociatedObjectOnClick(object sender, RoutedEventArgs routedEventArgs)
    {
        var flyout = AssociatedObject.GetVisualParent<FlyoutPresenter>();
        if (flyout != null)
        {
            var popup = flyout.Parent as Popup;
            if (popup != null)
            {
                popup.IsOpen = false;
            }
        }
    }
}

Note that this uses the Behavior base class I previously wrote about.

And if you’d like to make this into an action that can work for anything.

public class CloseFlyoutAction : IAction
{
    public object Execute(object sender, object parameter)
    {
        var element = sender as FrameworkElement;
        if (element == null) return null;
 
        var flyout = element.GetVisualParent<FlyoutPresenter>();
        if (flyout != null)
        {
            var popup = flyout.Parent as Popup;
            if (popup != null)
            {
                popup.IsOpen = false;
            }
        }
        return null;
    }
}