Visually Located

XAML and GIS

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