Visually Located

XAML and GIS

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;
    }
}

Animating list items within Windows apps

In a previous post I’ve talked about some page transitions and animations within Windows apps. There are many other locations where adding animations is very easy. One of these is animating items within any ItemsControl class. This includes controls like the ListView/GridView controls. The ItemsControl exposes the ItemContainerTransitions property. This property exposes a TransitionCollection, the same collection I talked about in the previous post. One of the key transitions you’ll want to look at is the EntranceThemeTransition.

The EntranceThemeTransition defines how items will enter the region when they are rendered. It contains three properties. FromVerticalOffset and FromHorizontalOffset define the where the item should start rendering, with respect to where the final location will be. So if you have a FromVerticalOffset of 100, the item will begin an entrance animation 100 pixels below where it will end. The last property IsStaggeringEnabled, defines if all items should animate at once, or if the animation should be staggered. I really like setting this to true for some great animations.

 

Here are some examples of using the EntranceThemeTransition to animate items.

Animate a list of items from the right

Set FromVerticalOffset to 0, and FromHorizontalOffset to your desired value.

<ListView.ItemContainerTransitions>
    <TransitionCollection>
        <EntranceThemeTransition IsStaggeringEnabled="True" 
                                 FromVerticalOffset="0" 
                                 FromHorizontalOffset="200"/>
        <AddDeleteThemeTransition/>
        <NavigationThemeTransition/>
        <ContentThemeTransition/>
    </TransitionCollection>
</ListView.ItemContainerTransitions>
Animate a list of items from the bottom

Set FromHorizontalOffset to 0, and FromVerticalOffset to your desired value.

<ListView.ItemContainerTransitions>
    <TransitionCollection>
        <EntranceThemeTransition IsStaggeringEnabled="True" 
                                 FromVerticalOffset="200" 
                                 FromHorizontalOffset="0"/>
        <AddDeleteThemeTransition/>
        <NavigationThemeTransition/>
        <ContentThemeTransition/>
    </TransitionCollection>
</ListView.ItemContainerTransitions>
Animate a list of items diagonally

Set both FromVerticalOffset  and FromHorizontalOffset to your desired value.

<ListView.ItemContainerTransitions>
    <TransitionCollection>
        <EntranceThemeTransition IsStaggeringEnabled="True" 
                                 FromVerticalOffset="200" 
                                 FromHorizontalOffset="200"/>
        <AddDeleteThemeTransition/>
        <NavigationThemeTransition/>
        <ContentThemeTransition/>
    </TransitionCollection>
</ListView.ItemContainerTransitions>
Animate a grid of items from the left

Set FromVerticalOffset  to 0, and  FromHorizontalOffset to a negative value.

<GridView.ItemContainerTransitions>
    <TransitionCollection>
        <EntranceThemeTransition IsStaggeringEnabled="True" 
                                 FromVerticalOffset="0" 
                                 FromHorizontalOffset="-200"/>
        <AddDeleteThemeTransition/>
        <NavigationThemeTransition/>
        <ContentThemeTransition/>
    </TransitionCollection>
</GridView.ItemContainerTransitions>

Play with the EntranceThemeTransition and come up with some great animations!

In depth look at the Windows RelativePanel

Windows 10 released a lot of new functionality and controls. One of the new controls is the RelativePanel. This panel takes the great Grid panel and cranks it up to 11.

The Grid panel gives a lot of control with how you layout controls. You specify rows and columns of various heights and widths and then place controls within the grid and define the row and/or column through attached properties. Instead of rows/columns, the RelativePanel places controls relative to itself or other controls within it. Just like the Grid, the RelativePanel uses 16 different attached properties to define where elements should be placed. In fact, the RelativePanel has no additional properties or methods from a regular Panel. It only contains attached properties.

Aligning relative to the RelativePanel

The relative panel does not respect controls with traditional alignment via HorizontalAlignment and VerticalAlignment. Instead there are six new properties to define how an element should align relative to the RelativePanel. These properties are boolean values that specify if it should align.

The following table shows the new attached properties.

Attached Property Default value Horizontal/VerticalAlignment equivalent
RelativePanel.AlignBottomWithPanel false VerticalAlignment=”Bottom”
RelativePanel.AlignHorizontalCenterWithPanel false HorizontalAlignment=”Center”
RelativePanel.AlignLeftWithPanel true HorizontalAlignment=”Left”
RelativePanel.AlignRightWithPanel false HorizontalAlignment=”Right”
RelativePanel.AlignTopWithPanel true VerticalAlignment=”Top”
RelativePanel.AlignVerticalCenterWithPanel false VerticalAlignment=”Center”

 

You can combine these values just as you could with HorizontalAlignment and VerticalAlignment. If you would like to center a control in the panel:

<RelativePanel>
    <Border Width="100" Height="100" Background="Blue" 
            RelativePanel.AlignHorizontalCenterWithPanel="True"
            RelativePanel.AlignVerticalCenterWithPanel="True"/>
</RelativePanel>

Center

By themselves, these six new properties are not very exciting. They are equivalent to the HorizontalAlignment and VerticalAlignment properties. The awesomeness comes from aligning with other elements.

NOTE: If you are following along with the blog, you will notice the designer, in both Visual Studio and Blend, will only render elements with the above properties correctly. It will not render elements with the properties below correctly. You will need to run the application to see the layout.

Align relative to other elements

This is where the RelativePanel shines. With the Grid control you had to create many different rows and columns. You had to know the row and/or column of a particular control. Then you assign the row and/or column of a control you wanted next to the first. Here is an example displaying name of someone

<TextBlock Text="Name" Grid.Row="1" Grid.Column="1"/>
<TextBlock Text="Shawn Kendrot" Grid.Row="1" Grid.Column="2" Margin="12,0,0,0"/>

Here is how you would accomplish that with the RelativePanel

<TextBlock x:Name="NameText" Text="Name"/>
<TextBlock Text="Shawn Kendrot" RelativePanel.RightOf="NameText" Margin="12,0,0,0"/>

When you want to align one control to the right of another another, simple say so!

Let’s take a look at the 10 new attached properties that allow for relative placement of one control with another.

Above

Aligns an element vertically above another element. This alone will not place an element directly above an element.

<RelativePanel>
    <Border x:Name="ElementOne" Width="200" Height="200" Background="Red" 
            RelativePanel.AlignHorizontalCenterWithPanel="True"
            RelativePanel.AlignVerticalCenterWithPanel="True"/>
    <Border x:Name="ElementTwo" Width="100" Height="100" Background="Blue" 
            RelativePanel.Above="ElementOne"/>
</RelativePanel>
Above-Windows

AlignBottomWith

Vertically aligns the bottom of a control with the bottom of another control. This property will not affect the horizontal alignment of the control.

<RelativePanel>
    <Border x:Name="ElementOne" Width="200" Height="200" Background="Red" 
            RelativePanel.AlignHorizontalCenterWithPanel="True"
            RelativePanel.AlignVerticalCenterWithPanel="True"/>
    <Border x:Name="ElementTwo" Width="100" Height="100" Background="Blue" 
            RelativePanel.AlignBottomWith="ElementOne"/>
</RelativePanel>

BottomWith

AlignHorizontalCenterWith

Horizontally aligns the center of one control with the center of another control. This property will not affect vertical alignment of the control.

<RelativePanel>
    <Border x:Name="ElementOne" Width="200" Height="200" Background="Red" 
            RelativePanel.AlignHorizontalCenterWithPanel="True"
            RelativePanel.AlignVerticalCenterWithPanel="True"/>
    <Border x:Name="ElementTwo" Width="100" Height="100" Background="Blue" 
            RelativePanel.AlignHorizontalCenterWith="ElementOne"/>
</RelativePanel>

HorizontalCenterWith

AlignLeftWith

Horizontally aligns the left edge of the control with the left edge of another control. This will not affect vertical alignment.

<RelativePanel>
    <Border x:Name="ElementOne" Width="200" Height="200" Background="Red" 
            RelativePanel.AlignHorizontalCenterWithPanel="True"
            RelativePanel.AlignVerticalCenterWithPanel="True"/>
    <Border x:Name="ElementTwo" Width="100" Height="100" Background="Blue" 
            RelativePanel.AlignLeftWith="ElementOne"/>
</RelativePanel>

LeftWith

AlignRightWith

Horizontally aligns the right edge of the control with the right edge of another control. This will not affect vertical alignment.

<RelativePanel>
    <Border x:Name="ElementOne" Width="200" Height="200" Background="Red" 
            RelativePanel.AlignHorizontalCenterWithPanel="True"
            RelativePanel.AlignVerticalCenterWithPanel="True"/>
    <Border x:Name="ElementTwo" Width="100" Height="100" Background="Blue" 
            RelativePanel.AlignRightWith="ElementOne"/>
</RelativePanel>

RightWith

AlignTopWith

Vertically aligns the top edge of the control with the top edge of another control. This will not affect horizontal alignment.

<RelativePanel>
    <Border x:Name="ElementOne" Width="200" Height="200" Background="Red" 
            RelativePanel.AlignHorizontalCenterWithPanel="True"
            RelativePanel.AlignVerticalCenterWithPanel="True"/>
    <Border x:Name="ElementTwo" Width="100" Height="100" Background="Blue" 
            RelativePanel.AlignTopWith="ElementOne"/>
</RelativePanel>

TopWith

AlignVerticalCenterWith

Vertically aligns the center of one control with the center of another control. This will not affect horizontal alignment.

<RelativePanel>
    <Border x:Name="ElementOne" Width="200" Height="200" Background="Red" 
            RelativePanel.AlignHorizontalCenterWithPanel="True"
            RelativePanel.AlignVerticalCenterWithPanel="True"/>
    <Border x:Name="ElementTwo" Width="100" Height="100" Background="Blue" 
            RelativePanel.AlignVerticalCenterWith="ElementOne"/>
</RelativePanel>

VerticalCenterWith

Below

Vertically aligns an element below another element. This alone will not place an element directly below an element. This will align the top edge of one control with the bottom edge of another control.

<RelativePanel>
    <Border x:Name="ElementOne" Width="200" Height="200" Background="Red" 
            RelativePanel.AlignHorizontalCenterWithPanel="True"
            RelativePanel.AlignVerticalCenterWithPanel="True"/>
    <Border x:Name="ElementTwo" Width="100" Height="100" Background="Blue" 
            RelativePanel.Below="ElementOne"/>
</RelativePanel>

Below

LeftOf

Horizontally aligns the right edge of one control with the left edge of another control.

<RelativePanel>
    <Border x:Name="ElementOne" Width="200" Height="200" Background="Red" 
            RelativePanel.AlignHorizontalCenterWithPanel="True"
            RelativePanel.AlignVerticalCenterWithPanel="True"/>
    <Border x:Name="ElementTwo" Width="100" Height="100" Background="Blue" 
            RelativePanel.LeftOf="ElementOne"/>
</RelativePanel>

LeftOf

RightOf

Horizontally aligns the left edge of one control with the right edge of another control.

<RelativePanel>
    <Border x:Name="ElementOne" Width="200" Height="200" Background="Red" 
            RelativePanel.AlignHorizontalCenterWithPanel="True"
            RelativePanel.AlignVerticalCenterWithPanel="True"/>
    <Border x:Name="ElementTwo" Width="100" Height="100" Background="Blue" 
            RelativePanel.RightOf="ElementOne"/>
</RelativePanel>

RightOf

Combining properties

Combine the alignment properties for the true power of the RelativePanel. Align the side and top/bottom edges of a control with another for exact layout.

<RelativePanel>
    <Border x:Name="ElementOne" Width="200" Height="200" Background="Red" 
            RelativePanel.AlignHorizontalCenterWithPanel="True"
            RelativePanel.AlignVerticalCenterWithPanel="True"/>
    <TextBlock Text="Hello" Margin="12,0,0,0" 
               RelativePanel.RightOf="ElementOne" 
               RelativePanel.AlignTopWith="ElementOne"/>
    <TextBlock Text="World" Margin="12,0,0,0"
               RelativePanel.RightOf="ElementOne" 
               RelativePanel.AlignBottomWith="ElementOne"/>
</RelativePanel>

Combine

You can try combining multiple horizontal or vertical alignments. As you can imagine, trying to horizontally align with two or more properties will probably not work. It’s hard to align to the left of object one while also aligning right of another.

Precautions

It is possible for items to be rendered outside of the given area. Take the following example of an item aligned to the panels right, and another object aligned to the right of the first.

<RelativePanel>
    <Border x:Name="ElementOne" Width="200" Height="200" Background="Red" 
            RelativePanel.AlignRightWithPanel="True"/>
    <Border x:Name="ElementTwo" Width="100" Height="100" Background="Blue" 
            RelativePanel.RightOf="ElementOne"/>
</RelativePanel>

When this is rendered, the blue area will not be visible.

OffScreen

This is still the case if you limit the size of the RelativePanel. If an item is out of the panel, it will not render.

Layout cascades from one item to another. So if object one aligns with object two, and object three aligns with object two, if object one moves, then object two and three move along with it.

This new controls offers great possibilities. What will you create with it?

Creating different page views for Windows Apps based on the device

Windows 10 has released the new Universal App Platform that brings new “Windows Apps”. These apps offer the ability to use one Visual Studio project to create apps for any platform/device. This offers the ability to have one XAML file for all of your views. This works much like the web does with responsive design. If you want your page to look different on a smaller device you can change the layout based on the width of the device. You can do this with StateTriggers.

<VisualStateManager.VisualStateGroups>
    <VisualStateGroup>
        <VisualState x:Name="wideState">
            <VisualState.StateTriggers>
                <AdaptiveTrigger MinWindowWidth="600"/>
            </VisualState.StateTriggers>
            <VisualState.Setters>
                <Setter Target="MyTextBlock.Text" Value="BIGGER!" />
            </VisualState.Setters>
        </VisualState>
        <VisualState x:Name="narrowState">
            <VisualState.StateTriggers>
                <AdaptiveTrigger MinWindowWidth="0"/>
            </VisualState.StateTriggers>
            <VisualState.Setters>
                <Setter Target="MyTextBlock.Text" Value="little" />
            </VisualState.Setters>
        </VisualState>
    </VisualStateGroup>
</VisualStateManager.VisualStateGroups>

The example above changes the text of a TextBlock based on the width of the app. You can find more StateTriggers from Morten Nielsen and others on his github page.

But what if you do not want one xaml file? What if you want a completely different view for a phone and don’t want to base it on the width of the device? In Windows and Windows Phone 8.1 you had separate projects that could hold different views based on the device. This is still possible! And it’s quite easy to accomplish. There are two main steps

First, create a new folder in your Windows 10 app and call it “DeviceFamily-Mobile”.

Folder_thumb

Then right click the folder and select Add –> New Item and select the top item “Xaml View”. Name the file MainPage.xaml.

mainpage_thumb

This creates just the xaml page of the page, not the code behind. It doesn’t need a code behind file because you’ve already declared a MainPage file!

That’s all you have to do! Now you have a page that is specific for mobile devices and a page that will be used for all other platforms/devices. Let’s test this out.

Open the mobile page and add a TextBlock stating we are on a phone.

<TextBlock Text="Hello from Windows Phone!"/>

Now open the other MainPage file and add the following:

<TextBlock Text="Hello from Windows!"/>

Run the app on a phone emulator or a device and you will see the first message!

phone[3]_thumb

Now run the app on your machine or the simulator and you will be greeted with the second message!

windows_thumb

Let’s take this one step further and add a device folder for desktop. This time name it “DeviceFamily-Desktop”. Add a new Xaml View and add the following to the Grid.

<TextBlock Text="Hello from Windows Desktop!"/>

Run the app on your machine and you will see the new text. The original MainPage.xaml will only display on a device that is not phone (mobile) and not desktop.

I mentioned already that the code file is shared. Anything you put in the file can be used for all of the pages. Let’s say you have a button on each view and need a click handler. One click handler in the code file will work for all views!

I’m sure there are more device family folders that would work like XBox or HoloLens but we’ll have to wait to test those.

Registering to any DependencyProperty changing in Windows 10 Apps

Many have argued that there are pieces still missing from Windows Runtime XAML that were in WPF. One item that was in WPF was the ability to be notified when a DependencyProperty changed. This functionality is now available in Windows Apps thanks to the new RegisterProperrtyChangedCallback method on DependencyObject. This opens up a world of opportunities for us. This functionality is extremely useful when creating custom controls or wrapping existing controls.

Rather than getting into anything complex, I’ll show a quick sample. A TextBlock control has Text, but no way to be notified when the text changes. We do have the ability to bind to the Text, but we’ll ignore that for now.

We’ll create two TextBlocks and one Button.

<StackPanel>
    <TextBlock x:Name="CounterText"/>
    <Button Content="Click me" Click="OnButtonClicked"/>
    <TextBlock x:Name="DuplicateTextBlock"/>
</StackPanel>

When the button is clicked we’ll set the text for the first TextBlock.

private int _counter;
 
private void OnButtonClicked(object sender, RoutedEventArgs e)
{
    CounterText.Text = string.Format("Clicked {0} times", ++_counter);
}

We’ll also register a callback for when the Text property changes for the CounterText TextBlock. Within the callback we’ll set the text of the other TextBlock.

public MainPage()
{
    this.InitializeComponent();
 
    CounterText.RegisterPropertyChangedCallback(TextBlock.TextProperty, OnTextChanged);
}
 
private void OnTextChanged(DependencyObject sender, DependencyProperty dp)
{
    var t = (TextBlock)sender;
 
    DuplicateTextBlock.Text = t.Text;
}

Now every time you click the button, it will set the text of the first TextBlock and the callback will fire setting the text of the second TextBlock!

capture-2

The benefit of an AdDuplex campaign

I recently had the opportunity to give 11 lucky winners $200 in AdDuplex credits. They are allowed to use these credits for anything they want. I wanted to highlight the benefits of using those credits for a campaign.

Last year I wanted to boost the downloads of my app 1:Clock. I decided to try a campaign through AdDuplex. I invested in a campaign for about three weeks. During that time I stopped and started the campaign. I targeted different country options and varied the amount of impressions. Overall I was very happy with the number of downloads I got during this period.

downloads

The timelines are as follows

  1. Started the campaign
  2. US market focus only
  3. Worldwide market focus
  4. Lower impression amount
  5. Raise impression amount
  6. Campaign ended

After the campaign ended my downloads were a little higher than before but did not maintain the download count I saw during the campaign. I did get quite a few reviews during this time which really helped out the app going forward! If you have some marketing budget, I would suggest trying out AdDuplex.

Enabling and tracking location in Windows Apps

Not much has changed with location in Windows Apps. You still use a Geolocator and still subscribe to the PositionChanged event or get one time location with the GetPositionAsync method. However I had problems getting started with a new Windows 10 project. I subscribed to the PositionChanged event, but it never fired. I subscribed to the StatusChanged event and found location was disabled. I was originally testing on the phone emulator and found that phone apps accessing location will now prompt the user.

So the phone prompted me for access, why is location disabled? Turns out you still need to add the location capability to your manifest file. There is not yet a GUI editor for the manifest file, so you must edit it manually. In the Capabilities element, add the following:

<DeviceCapability Name="location" />
Now run the app and you will have location data streaming in!

If you are running your app on a desktop or tablet, you will NOT be prompted for location access within the device capability. This removes some of the confusion. Hopefully this behavior will be consistent come RTM.

Creating a WrapPanel for your Windows Runtime apps

Recently I saw a friend, Glenn Versweyveld, write a blog about showing a “tags” (eg blog tags) within an app. The blog documents how to create a horizontal list of tags that are not formed into columns and rows. The list would let the items flow naturally. He used something I never would have thought of to accomplish this. He used a RichTextBlock. This was a rather cool idea that again, I would have never thought of.

When I saw the blog I quickly asked why he did not just use a GridView or a WrapGrid/ItemsWrapGrid. His simple reply was that it did not accomplish what he wanted due to the row/column layout..

If you are on “Big Windows” the GridView lays items out into columns and rows, by filling up columns from left to right. If you are on Windows Phone the Grid View also lays items in rows and columns, but it fills up rows first instead of columns.

The right picture shows Big Windows and the left shows phone.

GridView-BigWindowsGridView-Phone

Ok, so GridView is out, how about a ListView and change the ItemsPanel to be an ItemsWrapGrid with Orientation set to Horizontal? Nope, same row/column layout with that as well. Okay, now I see why Glenn went a custom route.

I like “Plug-n-Play” solutions. I like custom controls that I can put into XAML w/o and custom work. So, while I think Glenn’s approach was rather cool, it’s just not Plug-n-Play. To accomplish this wrapping we don't need to create a custom control. We can create a new Panel that we can use for any ItemsControl.

public class WrapPanel : Panel
{
    // 
}

When creating custom panels, there are two methods you must override, MeasureOverride and ArrangeOverride. The MeasureOverride method is where the panel determines how much space it will take up. It does this by asking each element within it to measure itself and then it will return the final size. The ArrangeOverride method is where the panel takes the information from the MeasureOverride and then places each item at X and Y locations.

The MeasureOverride will find the Height of the panel and just assume that the Width is the width it is given.

protected override Size MeasureOverride(Size availableSize)
{
    // Just take up all of the width
    Size finalSize = new Size { Width = availableSize.Width };
    double x = 0;
    double rowHeight = 0d;
    foreach (var child in Children)
    {
        // Tell the child control to determine the size needed
        child.Measure(availableSize);
 
        x += child.DesiredSize.Width;
        if (x > availableSize.Width)
        {
            // this item will start the next row
            x = child.DesiredSize.Width;
 
            // adjust the height of the panel
            finalSize.Height += rowHeight;
            rowHeight = child.DesiredSize.Height;
        }
        else
        {
            // Get the tallest item
            rowHeight = Math.Max(child.DesiredSize.Height, rowHeight);
        }
    }
 
    // Add the final height
    finalSize.Height += rowHeight;
    return finalSize;
}

The ArrangeOverride will place each item at the correct X and Y location based on the size of the elements.

protected override Size ArrangeOverride(Size finalSize)
{
    Rect finalRect = new Rect(0, 0, finalSize.Width, finalSize.Height);
 
    double rowHeight = 0;
    foreach (var child in Children)
    {
        if ((child.DesiredSize.Width + finalRect.X) > finalSize.Width)
        {
            // next row!
            finalRect.X = 0;
            finalRect.Y += rowHeight;
            rowHeight = 0;
        }
        // Place the item
        child.Arrange(new Rect(finalRect.X, finalRect.Y, child.DesiredSize.Width, child.DesiredSize.Height));
 
        // adjust the location for the next items
        finalRect.X += child.DesiredSize.Width;
        rowHeight = Math.Max(child.DesiredSize.Height, rowHeight);
    }
    return finalSize;
}

This panel will now layout items from left to right and any content that doesn’t fit in the row will go to the next row.

Let’s test this out. First we’ll try using an ItemsControl

<ItemsControl ItemsSource="{Binding Items}">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <controls:WrapPanel/>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Button Background="Red" MinWidth="0" MinHeight="0" Margin="0,0,6,0">
                <TextBlock Text="{Binding}" FontSize="20"/>
            </Button>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

We get the following layout in both Phone and Windows

WrapPanel-BigWindows

If you use a ListView or ListBox you will get a slightly different layout due to the style of the ListViewItem and ListBoxItem. I’ll let you decide how you want to style those.

Why does my ListView scroll to the top when navigating backwards?

I’ve seen a few people asking this question. They have a page which contains a ListView and when an item is selected it navigates to another page. When they navigate backwards the ListView is back up to the top again. This behavior is due to the NavigationCacheMode of the page. By default the page will not cache rendered content when navigating “forward”. So when you navigate back to the page it re-renders the content. When displaying content like a ListView this will cause it to show the top content.

navigation

As with most things, there are a few solutions to this problem. The most common solution is to set the NaivationCacheMode to Enabled or Required.

public ListPage()
{
    this.InitializeComponent();
 
    this.NavigationCacheMode = NavigationCacheMode.Required;
}

These options do the following:

Member Value Description
Disabled 0

The page is never cached and a new instance of the page is created on each visit.

Required 1

The page is cached and the cached instance is reused for every visit regardless of the cache size for the frame.

Enabled 2

The page is cached, but the cached instance is discarded when the size of the cache for the frame is exceeded.

With this property set for the page the page content will not re-render because the rendered state has been cached!

It does get a little annoying to set the property for every page. I like to use a base page class that contains all my navigation stuff and cache mode as well. This makes it much easier to do the basic stuff.

public class AppPage : Page
{
    public AppPage()
    {
        NavigationCacheMode = NavigationCacheMode.Enabled;
 
        // other stuff for navigation
    }
}
 
public partial class ListPage : AppPage
{
    ...
}

Unfortunately this is not a dependency property so you cannot create a base style that sets this property.

A second option is to use the ScrollIntoView method of the ListView. When your page loads, simply scroll your current item into view. This does have the drawback of not being at the exact same spot as before so I do recommend using the NavigationCacheMode.