XAML and GIS

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.

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.

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 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.

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>

<EntranceThemeTransitionIsStaggeringEnabled="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>

<EntranceThemeTransitionIsStaggeringEnabled="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>

<EntranceThemeTransitionIsStaggeringEnabled="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>

<EntranceThemeTransitionIsStaggeringEnabled="True"

FromVerticalOffset="0"

FromHorizontalOffset="-200"/>

<AddDeleteThemeTransition/>

<NavigationThemeTransition/>

<ContentThemeTransition/>

</TransitionCollection>

</GridView.ItemContainerTransitions>

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

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>

<BorderWidth="100"Height="100"Background="Blue"

RelativePanel.AlignHorizontalCenterWithPanel="True"

RelativePanel.AlignVerticalCenterWithPanel="True"/>

</RelativePanel>

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

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>

<Borderx:Name="ElementOne"Width="200"Height="200"Background="Red"

RelativePanel.AlignHorizontalCenterWithPanel="True"

RelativePanel.AlignVerticalCenterWithPanel="True"/>

<Borderx:Name="ElementTwo"Width="100"Height="100"Background="Blue"

RelativePanel.Above="ElementOne"/>

</RelativePanel>

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>

<Borderx:Name="ElementOne"Width="200"Height="200"Background="Red"

RelativePanel.AlignHorizontalCenterWithPanel="True"

RelativePanel.AlignVerticalCenterWithPanel="True"/>

<Borderx:Name="ElementTwo"Width="100"Height="100"Background="Blue"

RelativePanel.AlignBottomWith="ElementOne"/>

</RelativePanel>

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>

<Borderx:Name="ElementOne"Width="200"Height="200"Background="Red"

RelativePanel.AlignHorizontalCenterWithPanel="True"

RelativePanel.AlignVerticalCenterWithPanel="True"/>

<Borderx:Name="ElementTwo"Width="100"Height="100"Background="Blue"

RelativePanel.AlignHorizontalCenterWith="ElementOne"/>

</RelativePanel>

AlignLeftWith

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

<RelativePanel>

<Borderx:Name="ElementOne"Width="200"Height="200"Background="Red"

RelativePanel.AlignHorizontalCenterWithPanel="True"

RelativePanel.AlignVerticalCenterWithPanel="True"/>

<Borderx:Name="ElementTwo"Width="100"Height="100"Background="Blue"

RelativePanel.AlignLeftWith="ElementOne"/>

</RelativePanel>

AlignRightWith

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

<RelativePanel>

<Borderx:Name="ElementOne"Width="200"Height="200"Background="Red"

RelativePanel.AlignHorizontalCenterWithPanel="True"

RelativePanel.AlignVerticalCenterWithPanel="True"/>

<Borderx:Name="ElementTwo"Width="100"Height="100"Background="Blue"

RelativePanel.AlignRightWith="ElementOne"/>

</RelativePanel>

AlignTopWith

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

<RelativePanel>

<Borderx:Name="ElementOne"Width="200"Height="200"Background="Red"

RelativePanel.AlignHorizontalCenterWithPanel="True"

RelativePanel.AlignVerticalCenterWithPanel="True"/>

<Borderx:Name="ElementTwo"Width="100"Height="100"Background="Blue"

RelativePanel.AlignTopWith="ElementOne"/>

</RelativePanel>

AlignVerticalCenterWith

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

<RelativePanel>

<Borderx:Name="ElementOne"Width="200"Height="200"Background="Red"

RelativePanel.AlignHorizontalCenterWithPanel="True"

RelativePanel.AlignVerticalCenterWithPanel="True"/>

<Borderx:Name="ElementTwo"Width="100"Height="100"Background="Blue"

RelativePanel.AlignVerticalCenterWith="ElementOne"/>

</RelativePanel>

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>

<Borderx:Name="ElementOne"Width="200"Height="200"Background="Red"

RelativePanel.AlignHorizontalCenterWithPanel="True"

RelativePanel.AlignVerticalCenterWithPanel="True"/>

<Borderx:Name="ElementTwo"Width="100"Height="100"Background="Blue"

RelativePanel.Below="ElementOne"/>

</RelativePanel>

LeftOf

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

<RelativePanel>

<Borderx:Name="ElementOne"Width="200"Height="200"Background="Red"

RelativePanel.AlignHorizontalCenterWithPanel="True"

RelativePanel.AlignVerticalCenterWithPanel="True"/>

<Borderx:Name="ElementTwo"Width="100"Height="100"Background="Blue"

RelativePanel.LeftOf="ElementOne"/>

</RelativePanel>

RightOf

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

<RelativePanel>

<Borderx:Name="ElementOne"Width="200"Height="200"Background="Red"

RelativePanel.AlignHorizontalCenterWithPanel="True"

RelativePanel.AlignVerticalCenterWithPanel="True"/>

<Borderx:Name="ElementTwo"Width="100"Height="100"Background="Blue"

RelativePanel.RightOf="ElementOne"/>

</RelativePanel>

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>

<Borderx:Name="ElementOne"Width="200"Height="200"Background="Red"

RelativePanel.AlignHorizontalCenterWithPanel="True"

RelativePanel.AlignVerticalCenterWithPanel="True"/>

<TextBlockText="Hello"Margin="12,0,0,0"

RelativePanel.RightOf="ElementOne"

RelativePanel.AlignTopWith="ElementOne"/>

<TextBlockText="World"Margin="12,0,0,0"

RelativePanel.RightOf="ElementOne"

RelativePanel.AlignBottomWith="ElementOne"/>

</RelativePanel>

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>

<Borderx:Name="ElementOne"Width="200"Height="200"Background="Red"

RelativePanel.AlignRightWithPanel="True"/>

<Borderx:Name="ElementTwo"Width="100"Height="100"Background="Blue"

RelativePanel.RightOf="ElementOne"/>

</RelativePanel>

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

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?

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>

<VisualStatex:Name="wideState">

<VisualState.StateTriggers>

<AdaptiveTriggerMinWindowWidth="600"/>

</VisualState.StateTriggers>

<VisualState.Setters>

<SetterTarget="MyTextBlock.Text"Value="BIGGER!"/>

</VisualState.Setters>

</VisualState>

<VisualStatex:Name="narrowState">

<VisualState.StateTriggers>

<AdaptiveTriggerMinWindowWidth="0"/>

</VisualState.StateTriggers>

<VisualState.Setters>

<SetterTarget="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”.

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

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.

<TextBlockText="Hello from Windows Phone!"/>

Now open the other MainPage file and add the following:

<TextBlockText="Hello from Windows!"/>

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

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

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.

<TextBlockText="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.

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>

<TextBlockx:Name="CounterText"/>

<ButtonContent="Click me"Click="OnButtonClicked"/>

<TextBlockx:Name="DuplicateTextBlock"/>

</StackPanel>

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

privateint _counter;

privatevoid 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.

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.

The timelines are as follows

Started the campaign

US market focus only

Worldwide market focus

Lower impression amount

Raise impression amount

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.

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:

<DeviceCapabilityName="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.

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.

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.

publicclass 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.

protectedoverride 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.

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.

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.

publicclass AppPage : Page

{

public AppPage()

{

NavigationCacheMode = NavigationCacheMode.Enabled;

// other stuff for navigation

}

}

publicpartialclass 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.