Archive for July 27th, 2015
UWP: Extensions and ApiInformation
Universal Windows Platform allows to build universal applications for all devices which run Windows 10. I have already published several posts about new controls which allow to create universal interfaces for different screen sizes and resolutions. But what about device-specific features? For example: Raspberry Pi has set of pins which are not available for phone and tablets; you can use desktops and laptops in order to print something directly from your application but this functionality is not available for phones; lots of phones support vibration what is very uncommon for laptops etc. That’s why all contracts with device-specific functionality were moved to special libraries called extensions.
You can include existing extensions to your project using Add Reference dialog and open Extensions tabs. Today you can find three most important extensions there like Mobile Extension, Desktop Extension and IoT Extension.
Of course you might see more extensions soon. For example, Microsoft might publish some extensions for Xbox and Hololens but you will be able to create your own for own Windows 10 devices.
If you want to see all contracts which were included to the extension, you can open the manifest file for the selected extension and check all contracts there. For example, you can find the IoT manifest using the following folder C:\Program Files (x86)\Windows Kits\10\Extension SDKs\WindowsIoT\10.0.10069.0 and manifest looks like
<?xml version="1.0" encoding="utf-8"?> <FileList TargetPlatform="UAP" TargetPlatformMinVersion="10.0.0.1" TargetPlatformVersion="10.0.10069.0" SDKType="Platform" DisplayName="Windows IoT Extension SDK" AppliesTo="WindowsAppContainer" MinVSVersion="14.0" ProductFamilyName="Windows.IoT" SupportsMultipleVersion="Error" TargetFramework=".NETCore, version=v4.5.3;" SupportPrefer32Bit="True" MoreInfo="http://www.microsoft.com/en-us/server-cloud/internet-of-things.aspx"> <ContainedApiContracts> <ApiContract name="Windows.Devices.DevicesLowLevelContract" version="1.0.0.0"/> <ApiContract name="Windows.System.SystemManagementContract" version="1.0.0.0"/> </ContainedApiContracts> </FileList>
So, right now there are two contracts only and you can find all classes there using Object Browser in the Visual Studio.
I already published how to use IoT extensions for Raspberry Pi 2. So, in this article I want to talk more about how to implement universal approach even if you add an extension.
The main idea of Universal Windows Platform is to give a way to create just one application and one binary for all devices. But what happens when we add an extension? Should we recompile our application for different platforms with this extension and without the extension? The answer is No.
Extensions are designed in the way when you can continue to run your code on a platform which doesn’t support extensions which you have included to your project. But if you try to call methods and create objects based on classes from the extensions, these calls will generate runtime exception.
That’s why you need to check in runtime if selected extension is available on the platform. Of course, you should not do it in exception handler. It’s better to adopt the interface of your application for the specific device rather than notify users about some unsupported feature when the application is running. So, developers need to have better tool to check availability of contract and Microsoft implemented all needed features in ApiInformation class.
ApiInformation is located in Windows.Foundation.Metadata namespace and contains several static methods like IsApiContractPresent, IsEventPresent, IsMethodPresent etc.
So, if you want to check, if GPIO is available, you can use the following code:
if (ApiInformation.IsApiContractPresent("Windows.Devices.DevicesLowLevelContract", 1)) { //doing something with GPIO }
Pay special attention that IsApiContractPresent requires two parameters. The second one is major version (you can use minor version as well – third parameter). This parameter is needed because Microsoft can update some of contracts and you can target specific version(s).
The code above doesn’t mean that you need to create hundreds of if blocks if you include one or several contracts. It’s better to create a custom trigger based on this information and implement several visual states.
UWP: New Controls (part 4 – Inking)
One more powerful control in Universal Windows Platform is InkCanvas. Thanks to this control you can enable inking anywhere in your application and you can use not just stylus but fingers and mouse as well. So, thanks to this feature you can bring inking functionality to any device based on Windows 10.
To enable inking you need to start with InkCanvas element and place it inside any container like StackPanel, Grid, etc. Like other UI controls based on FrameworkElement, InkCanvas contains a bunch of properties but in the simplest case you do not have to declare anything:
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <InkCanvas Name="ink"></InkCanvas> </Grid>
In this case InkCanvas will fill all space inside the container and you can start make your notes or paint something. Pay special attention that by default InkCanvas accepts input from stylus (pen) only. So, if you want to use fingers or mouse you need to implement the following code:
ink.InkPresenter.InputDeviceTypes = CoreInputDeviceTypes.Mouse|CoreInputDeviceTypes.Touch;
In this code we are using the second important object – InkPresenter. InkCanvas is just a control which contains just one inking property – reference to InkPresenter but InkPresenter contains all information about input methods and lots of different settings there including collection of strokes. You cannot create InkPresenter directly but you can get reference to the object of this class using InkPresenter property of InkCanvas. Frankly speaking InkCanvas doesn’t contain any other properties or methods related to inking. But we still have something to discuss about InkCanvas and the most important question there is how to enable inking anywhere because usually you need to enable inking for images, videos, text rather than using inking inside a blank container. Let’s add several controls to the container and see what happens there.
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <InkCanvas Name="ink"></InkCanvas> <StackPanel> <TextBlock Text="Hello. Here is some text." Margin="20"></TextBlock> <Button Content="Click me" Margin="20"></Button> </StackPanel> </Grid>
If you run this code you will see that all controls work fine and you can make any notes there.
In our example we placed InkCanvas behind other controls but if you swap StackPanel and InkCanvas you can see that InkCanvas is placed above StackPanel and controls like Button don’t work at all. You can resolve it thanks to Canvas.ZIndex property.
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <StackPanel Canvas.ZIndex="1"> <TextBlock Text="Hello. Here is some text." Margin="20"></TextBlock> <Button Content="Click me" Margin="20"></Button> </StackPanel> <InkCanvas Name="ink" ></InkCanvas> </Grid>
Let’s talk about settings of InkPresenter and how to make different effects there. It’s all around InkDrawingAttributes class. You simply need to create an object of this class and set different properties like Color, PenTip, Size etc.
InkDrawingAttributes attr = new InkDrawingAttributes(); attr.Color = Colors.Red; attr.IgnorePressure = true; attr.PenTip = PenTipShape.Circle; attr.Size = new Size(4, 10); attr.PenTipTransform = Matrix3x2.CreateRotation((float)(70 * Math.PI / 180)); ink.InkPresenter.UpdateDefaultDrawingAttributes(attr);
Code below shows how to use InkDrawingAttributes. In order to update attributes you need to call UpdateDefaultDrawingAttributes and pass InkDrawingAttributes object there. In code below I used PenTipTransform property as well. This is a very interesting property which allows to apply transformation to pin shape and get more natural look for strokes thanks to different height of strokes based on direction of pen (moving direction/angle).
You can check the image below where I tried to paint symbol “f” and you can see that the image contains strokes with different height. In fact I used the same settings but this effect was applied thanks to PenTipTransform.
Finally, you can paint anything but how can you erase some strokes if you make anything wrong? In order to do it you need to change mode of InkPresenter:
ink.InkPresenter.InputProcessingConfiguration.Mode = InkInputProcessingMode.Erasing;
The next important class which is related to inking is InkStroke. Anything what you paint on InkCanvas consists of InkStroke. In order to get access to functionality around InkStroke objects you can use StrokeContainer property of InkPresenter. Thanks to this property you can save existing strokes to file, add new strokes, get access to selected strokes etc. So, if you want to implement any functionality around strokes then StrokeContainer is your friend. Of course the most popular methods there are LoadAsync and SaveAsync.
Lots of applications require recognition functionality as well. In order to recognize text based on hand writing you can use InkRecognizerContainer class. In the simplest way you can use this class like this:
InkRecognizerContainer container = new InkRecognizerContainer(); var result=await container.RecognizeAsync(ink.InkPresenter.StrokeContainer, InkRecognitionTarget.All); string s=result[0].GetTextCandidates()[0];
This code will use default recognizer and convert the first text candidate from the list to text. Of course it’s not enough for more complex scenarios. For example, I spend 10 years at school to train my handwriting for Russian and Ukrainian but not for English. So, when I am trying to write something in English usually I can find the right result on the second or third place in the list of text candidates. Additionally, I can have several recognizers on my computer.
So, it’s better to allow to select the right recognizer. You can simply use SetDefaultRecognizer and GetRecognizers of InkRecognizerContainer object to do it. In case of non-native English writers I am not sure that you need to present full list of possible results. It’s better to ask users to setup recognizer using system settings. For example, I can setup the default recognizer to adopt to my writing style if I select “write each character separately” and make some “teach” settings:
At the end of the post I want to draw your attention to Ink Toolbar control. It’s not a default UWP control and you need to install this control separately. Once you install the control it will be available in Extension tab of Add Reference dialog (close and open Visual Studio after installation) and it’s ready to use. You just need to add reference to the library:
xmlns:ink="using:Microsoft.Labs.InkToolbarControl"
Find a place for the control and make reference to the existing InkCanvas:
<InkCanvas Name="ink" ></InkCanvas> <ink:InkToolbar TargetInkCanvas="{x:Bind ink}" VerticalAlignment="Top" HorizontalAlignment="Right"></ink:InkToolbar>
Below you can see the control itself which generates our code: