Browse Category

UWP

Rotate a picture.

Windows Photos application allows a user to rotate an image and save a copy to a local storage. How would you implement the same functionality in your app?

There are two approaches:

  1. We can get a raw pixel data of a picture and then rotate the array of bytes;
  2. Universal Windows Platform has API, which helps to solve this task.

Let’s take a look at both of them.

Access a raw pixel data.

To access a pixel data we have to utilize a chain of objects. First of all, we need to decode an input image data with an instance of BitmapDecoder. From BitmapDecoder, we can get an instance of PixelDataProvider. And then, use PixelDataProvider.DetachPixelData to get an array of pixel data.

The code bellow demonstrates how to do it:

Rotate a pixel data clockwise.

A picture rotation implies rotation of a two-dimensional matrix. Such kind of a transformation means that columns of an input array are converted to rows of an output array and rows of the input becomes columns of the output. Taking that in account, we can express the logic of a matrix transformation in the following code:

I set variable output to a two-dimensional array of int and used number of columns as number of rows for the output. I’m using a row index of the output array to access columns of the input array. And index of an input row can be calculated from an output column index.

When dealing with a picture, we have a one-dimensional array of a pixel data and a pixel length typically is 4 bytes. The algorithm to rotate a one-dimensional array has 3 steps:

  1. Calculate row and column indexes of a two-dimensional matrix from an index of an input array;
  2. Calculate rotated row and column indexes;
  3. Use the values calculated at the step 2 and convert them to one-dimensional index.

I wrapped it up in RotateClockwise method:

The method takes pixel data and size of a picture. When rotated row and column indexes are calculated, I’m traversing a nested loop to copy values from an input pixel to a rotated pixel. By rotated pixel, I mean a pixel from the rotated matrix.

Save an image.

Now, we can save transformed matrix to a local storage. BitmapEncoder can help us with that. I call a staic method BitmapEncoder.CreateForTranscodingAsync to create an instance of encoderAnd afterwards I use BitmapEncoder.SetPixelData and BitmapEncoder.FlushAsync to apply a pixel data and save the result.

The height of the original image should be used as a value for width parameter and the width for height parameter when calling SetPixelData

Rotate using BitmapTransform.

The described approach of a rotation gives us basic understanding of simple transformations that can be applied on a bitmap. But in a real project we don’t need to manipulate with pixels. BitmapTransform class has a set of properties, which can modify a picture depending on our needs. I updated SaveRotateAsync to use BitmapTransform to achieve the desired result:

We can not only rotate, but flip and scale with the help of an instance of BitmapTransform class.

Wish you fruitful coding!

Horizontal swiping in ListViewBase control with mandatory, centered snap points.

You might need to implement a swiping behavior in a list similar to FlipView control. In FlipView, a swiping always ends at the center of an element. Unlike it works by default in ListViewBase control, where user can see many items at once and stopping points are randomly chosen. But FlipView is not really wanted, when it’s required to display left and right items. The behavior I needed to achieve:

Swiping behavior

 

Snap points type and alignment.

The easiest way to customize user interactions with a list is to configure ScrollViewer control. ScrollViewer has two properties, which allow to control horizontal swiping. It’s ScrollViewer.HorizontalSnapPointsType and ScrollViewer.HorizontalSnapPointsAlignment. MandatorySingle snap points type with Center alignment makes ScrollViewer always stop at the center of a list element.

 

Customizing horizontal swiping in GridView.

HorizontalSnapPointsType and HorizontalSnapPointsAlignment are not attached properties. They can’t be easily accessed from GridView.

Template of GridView is the best place to set MandatorySingle and Center values for internal ScrollViewer:


I also override ItemsPanel in this XAML, because I want to show items in a horizontal panel.

 

Example.

Take a look at the example here. Launch the app at Windows mobile emulator and hit Snap points example button. As soon as the next page is being loaded and a list with images is being shown, swipe to the second item and immediately to the first one. You will notice a bug. The first item becomes unreachable. I fixed the issue with inserting empty items in the beginning and the end of the list:

GridView.ScrollIntoView() allows to make the first not empty item logically focused:

To make the example works fine, go to HorizontalSnapPointsPage.xaml.cs and undo commented InsertEmptyItems() and ScrollToFirstItem() methods. Swiping in the list works as expected now.

Wish you fruitful coding!

One more time about localization.

To localize a UWP app, a developer needs to do a few things:

  • create Resources folder in the solution;
  • add a subfolder with the name of the app default locale, for example en-US;
  • set default locale in AppManifest;
  • add Resources.resw to the created subfolder;
  • utilize x:Uid directive in XAML to apply a resource value.

After the main functionality is ready, the same steps can be applied for locales you want to support in the app. You also might need to create a service to load and access the resources. But intent of this article is to take a deeper look at the internal structure of localized resources in a package, when an app is ready to be published to the store.

With Universal Windows Platform, a new way of an app distribution was introduced. Microsoft suggests to create a bundle when your app is ready for submission to the store. Appxbundle is an archive file, which contains app packages for: x86, x64 and ARM. What is also interesting about app bundle is that allows to manage resources included into a package. Usually, an app has string resources or images with different resolutions for different locales. And for sure, when a user installs an app, he doesn’t need everything at his device. That’s exactly what app bundle helps to solve. All resources in an app bundle are packed in some way and when a user downloads an app, he gets localized resources, related to his device language settings. Thus, users from USA won’t get German resources, because they don’t need them.

This image can give you some insights about internal structure of a bundle:

Apart of the files related to the locales, there is also AppManifest file which contains metadata about the resources.

When a user installs a package distributed as not a bundle, he gets all localized resources used in the app. And that’s the main difference between a bundle distribution and the distribution used for Windows 8.1 apps.

If localized resources are downloaded respectively with a device language settings, how then programmatically apply different locales for UI? What if users want to set a different language? A language which is not the same as in device settings? That’s a tricky part of appxbundle. Even, you might not notice any issue with that when developing your app. But when try to change a language when the app is installed from a bundle, you will see that not every locale is applied properly. That’s because end device doesn’t have all files with localized resources.

The described issue can be handled in different ways. One possible solution is implemented in Bing News app. When the user chooses the app language, it asks to add chosen language to the device settings and set it as display language. Afterwards, an update will be installed by Windows. I suspect that Windows also will update the app with required resources file from the store.

The second solution is to implement custom localization and apply a resource value with bindings instead of x:Uid directive.

The third solution is to publish your app in the old way, with a package per configuration. As I mentioned before, if create a package but not a bundle, all localized resource will be included to that package. Thus, this approach gives more flexibility while can significantly increase the size of downloaded package for final users.

Once you started to distribute an app as a bundle, it won’t be possible to switch to a package per configuration. Dev center forbids an update with separated packages above appxbundle.

Actually, that’s not always forbidden. Our team had a bad experience with that. We published an update as a bundle for existing Windows 8.1 app. In other words, new Windows 10 app as an update for Windows 8.1 app. Afterwards, we figured out that localization doesn’t work as we expected. We decided to switch to a package per configuration distribution. We did that update and when submitting, it was allowed to upload three separated packages. As soon as the update became available, the users started complain about a crash when the app was launching.

That was really painful. It took significant amount of time to figured out the reason. When an update is being installed, all localized resources are being merged. Thus, the system tried to merge the resources from the installed bundle with the resources included to a new package. Such merge is not possible. And it wasn’t easy to catch the reason.

The main lesson – don’t switch from a bundle to the old way of distribution. It’s very possible that you will get some issues with that. But migrating to a bundle from the old way of distribution works well.

Fruitful coding!

UWP: Back navigation

In Windows Universal app, when user has been navigated back, the top page at the navigation stack is being re-created again. That’s because a page itself is not kept in memory, when the user navigates forward to another page.

Let’s imagine an app. The first page of the app has a list. The user can scroll the list down and when he taps a list item, he has been redirected to the item details page. When the user presses back button, the main page is appeared. But the state of the page is not preserved. The user sees the same list with initial scroll viewer position. He has to scroll again to the item he left the page.

How to improve the described scenario to make the user happy? In our example, a developer has two options:

  1. Maintain ViewModel of the main page as Singleton and keep ScrollViewer vertical offset in the ViewModel;
  2. Enable UI cache for the main page.

I prefer the second one.

Navigation cache mode.

Page.NavigationCacheMode is the property, which allows a page to be stored in memory if the value of the property is set to Enabled or Required. In our case, the MainPage with enabled NavigationCacheMode, will be created once. Furthermore, the details page, with enabled navigation cache, is also created once and will be reused to show details of every data item. Thus, a page with NavigationCacheMode=Enabled stays in memory even the user navigates back or forward from the page.

Loaded EventTriggerBehavior and back navigation.

XamlBehaviors is a great library. It allows to get rid of event handlers in a page class and bind event handlers with commands exposed by ViewModel at XAML level. But be aware about using EventTriggerBehavior for Loaded event together with enabled NavigationCacheMode.

Let’s assume we have MainPage in our app with navigation cache is enabled:

 

In page XAML, there is EventTriggerBehavior which executes a command when the root element has been loaded. And a button, which navigates the user to another page, when it’s been clicked:

When the page is being loaded in the first time, LoadedCommand executes as expected. But what happens, when the user goes back from another page to the MainPage? If the user navigates back, Loaded event for the root Grid fires and I expect LoadedCommand to be executed as well. As you can guess, it doesn’t work in this way. When the user navigates back, EventTriggerBehavior is attached to the root element after the element has been loaded. So, for the described case, LoadedCommand never will be triggered.

For similar scenarios, it’s better to subscribe for FrameworkElement.Loaded manually and execute a command in code behind. Either, override Page.OnNavigatedTo and call your logic from there.

Fruitful coding!

UWP: BindingExpression errors in ListView with a heterogeneous collection

Recently, I got BindingExpression exceptions when was creating some UI with ListView:

Error: BindingExpression path error: ‘Weight’ property not found on ‘HeterogeneousListView.Data.Milk, HeterogeneousListView, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null’. BindingExpression: Path=’Weight’ DataItem=’HeterogeneousListView.Data.Milk, HeterogeneousListView, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null’; target element is ‘Windows.UI.Xaml.Controls.TextBlock’ (Name=’null’); target property is ‘Text’ (type ‘String’)

Error: BindingExpression path error: ‘Liters’ property not found on ‘HeterogeneousListView.Data.Cookie, HeterogeneousListView, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null’. BindingExpression: Path=’Liters’ DataItem=’HeterogeneousListView.Data.Cookie, HeterogeneousListView, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null’; target element is ‘Windows.UI.Xaml.Controls.TextBlock’ (Name=’null’); target property is ‘Text’ (type ‘String’)

Error: BindingExpression path error: ‘Weight’ property not found on ‘HeterogeneousListView.Data.Cookie, HeterogeneousListView, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null’. BindingExpression: Path=’Weight’ DataItem=’HeterogeneousListView.Data.Cookie, HeterogeneousListView, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null’; target element is ‘Windows.UI.Xaml.Controls.TextBlock’ (Name=’null’); target property is ‘Text’ (type ‘String’)

You might seen similar errors when trying to bind a data item to a wrong template. The difference in my case is that the UI looked well, without any visible issues. I prepared a test project to reproduce the bug. The code is at github.

The test application shows some heterogeneous data in a ListView. My test data is three objects: Cookie, Milk and Orange.

There are two lists in the app. The lists have various approaches of applying a DataTemplate for an ItemContainer. For the left ListView, I set the ListView.ItemContainerSelector property to an instance of my custom DataTemlateSelector:

As you noticed, I override DataTemplateSelector.SelectTemplateCore
to choose a template for a data item. If you scroll down the left list, you immediately will see the errors in Debug Output window of VisualStudio. The fact, that the errors are appearing when scrolling the list, made me think that the realization of items works wrong. And most probably the bug in ProductTemplateSelector. If uncomment the debug lines in ProductTemplateSelector.SelectTemplateCore method, the output will show something interesting:


Container for: Orange 3

Data Cookie 98

Error: BindingExpression path error: ‘Pieces’ property not found on ‘HeterogeneousListView.Data.Orange, HeterogeneousListView, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null’. BindingExpression: Path=’Pieces’ DataItem=’HeterogeneousListView.Data.Orange, HeterogeneousListView, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null’; target element is ‘Windows.UI.Xaml.Controls.TextBlock’ (Name=’null’); target property is ‘Text’ (type ‘String’)


Container for: Orange 4

Data Orange 99


Container for: Cookie 98

Data Cookie 98


Container for: Orange 99

Data Orange 99

It says that an ItemContainer is being reused for a data item and DataTemplate.SelectTemplateCore is being executed twice for the same data item.

In fact, DataTemplateSelector has two versions of SelectTemplateCore:

And which version a developer needs to utilize depends on underlying ItemsPanel. If you use GridView or ListView with ItemsStackPanel or with ItemsWrapGrid then SelectTemplateCore(Object) needs to be overriden. By default, the underlaying panel for ListView and GridView is ItemsStackPanel. But if ItemsPanel is VirtualizingStackPanel or WrapGrid then SelectTemplateCore(Object, DependencyObject) should be implemented. The rule applies to any ItemsControl.

I override the wrong version of SelectTemplateCore in my ProductTemplateSelector. That’s the origin of the error. Uncomment ProductTemplateSelector.SelectTemplateCore(Object) in the example, run the app. The list now works smoothly.

What I also want to show you is how to apply a DataTemplate for an ItemContainer without using ListView.ItemContainerSelector. Starting from Windows 10, ListView and GridView controls have a new  ListViewBase.ChoosingItemContainer event.

In the test app, the ListView with custom implementation of ChoosingItemContainer is shown at the right part of the MainPage. And the handler for the event is very simple:

 

Needless to say, that ChoosingItemContainer gives better control when creating an item container. DataTemplateSelector is very simple to use and it does what it does. But when it requires to gain some performance benefits, then the best way is to implement a handler for ChoosingItemContainer event. You can also get more ideas about the usage of the new event from MSDN.

Fruitful coding!

UWP: range selector control

Slider control lets the user to select a value from a range. What about selecting multiple values from a given minimum and maximum?

There is no possibility to tune Slider to work as it depicted above. That’s a case to implement a custom control. Before we start, download an example from here.

Main properties.

The new control is going to have minimum and maximum properties to restrict user input, start and end range to save what the user has selected. Let’s begin from introducing a new class RangeSelector with the required dependency properties:

Style and template.

When RangeSelector is added and inherited from Windows.UI.Xaml.Controls.Control, we can declare the style and template for the control. Any custom control style should be placed in Themes/Generic.xaml file:

Don’t forget to update constructor to apply the defined style when RangeSelector is being created:

The visual part of any control is a template. Slider control templae has two rectangles whith a thumb. A Slider thumb is Border element. The full template easy to get with Blend. A good article about how to do it is here.

My RangeSelector has two thumbs, left and right. I utilize two rectangles to apply selection effect. The first one, I use to visualize a set of available values. And another rectangle, to show a selected range. When the control is loaded, the first rectangle is not visible because the full range is selected. In the template, the both rectangles are placed at the same level of logical tree:

I use clipping to shrink or expand the track. The thumbs have RenderTransform set to TranslateTransform. My intent is to use TranslateTransform.X property to update a thumb position, when the user is dragging a thumb:

Finally, the completed style:

Control.Foreground property is used to highlight a selected range and Control.Background to show space available for dragging.

Handle user input.

Now, lets subscribe on DragStarted and DragCompleted events. The right place to do that is RangeSelector.OnApplyTemplate() method. Inside the method we can get references to the elements we defined in the style and add required events handlers:

LeftThumbDragDelta and RightThumbDragDelta are responsible for updating the horizontal position of the thumbs. There are two corner cases that we need to take care of:

  1. The thumbs can’t intersect;
  2. The thumbs should be steak to bounds of the track rectangle.

To prevent the intersection from the left side, I check if sum of the left thumb position and a horizontal delta is less than the right thumb X coordinate. For the right thumb, I only apply a shift if the right thumb X positioin is greater than the left thumb X position. To keep the both thumbs on the track, it’s enough to compare the sum of the current positoin and a horizontal change with the point (0,0). Less words, more action. The code bellow serves the described cases:

You already noticed that after a thumb position has been updated, there is a call of UpdateTrack() method. The method sets TrackRect clipping. To calculate the width between the thumbs, I transform the both thumbs poistion, subtract the right X from the left X and set the result to the clipping rectangle:

The RangeSelector is ready to use. The source code is available at github. Happy coding!