ScratchPad: a Windows Ink sample for Windows 10 UWP

One of the especially interesting features available in the Windows 10 UWP platform is "Windows Ink" - the ability to use a pen or stylus to draw onto the screen and capture and process the digital ink strokes.

[caption id="attachment_145" align="alignnone" width="300"]ScratchPad could be the start of your next app. ScratchPad could be the start of your next app.[/caption]

This example is a simple C# note-taking app, demonstrating how to add ink support to your own projects. It was written using Visual Studio 2015 Update 2, and it provides a single window into which you can draw with a pen, on, say, a Surface Book. It also supports a mouse. The ink strokes are saved when the app is suspended, and re-loaded when the app starts.

The very useful InkToolbar control is also included. It's important you follow the procedure to download and install this control, or your project will not build. The InkToolbar provides an easy way to change digital ink color and thickness. If you wish, you can omit it from the project by removing the references to InkToolbar in the XAML file.

Hopefully, ScratchPad can be the basis for your own experiments with Windows Ink.

Instructions

First, install the InkToolbar, by following the guidance here.

Next, create a new C# project, using the Blank App (Universal Windows) template. Call it "ScratchPad".

Now add references to the InkToolbar and the VC runtime, by right-clicking on the Project in the Solution Explorer window, selecting Add.., and then Reference… and highlighting them in the list. It should look like this:

[caption id="attachment_155" align="alignnone" width="300"]The Add Reference manager window. The Add Reference manager window.[/caption]

There are two source files you will need to edit: MainPage.cs, and MainPage.xaml. Simply replace the code created by the template with the files before, and you're done.

MainPage.xaml

<Page x:Class="ScratchPad.MainPage" xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:ScratchPad" xmlns:d="https://schemas.microsoft.com/expression/blend/2008" xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:inkTools="using:Microsoft.Labs.InkToolbarControl" mc:Ignorable="d" Width="Auto" Height="Auto"> <Grid x:Name="rootGrid" Background="#FFFCFECF" Margin="0" > <Viewbox Width="{x:Bind rootGrid.Width}" Height="{x:Bind rootGrid.Height}"> <Border Width="600" Height="400" BorderBrush="LightGray" BorderThickness="1"> <InkCanvas x:Name="inkCanvas"/> </Border> </Viewbox> <inkTools:InkToolbar TargetInkCanvas="{x:Bind inkCanvas}" PenColor="Black" VerticalAlignment="Top" HorizontalAlignment="Right"></inkTools:InkToolbar> </Grid> </Page>
 

 

MainPage.cs

using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Runtime.InteropServices.WindowsRuntime; using System.Threading.Tasks; using Windows.ApplicationModel; using Windows.ApplicationModel.Core; using Windows.Foundation; using Windows.Foundation.Collections; using Windows.Storage.Streams; using Windows.UI; using Windows.UI.Input.Inking; using Windows.UI.ViewManagement; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Controls.Primitives; using Windows.UI.Xaml.Data; using Windows.UI.Xaml.Input; using Windows.UI.Xaml.Media; using Windows.UI.Xaml.Navigation; namespace ScratchPad { /// /// A simple note-taking app, that saves and re-loads digital ink strokes. /// Works with both stylus and mouse. /// Includes support for the InkToolbar control /// public partial class MainPage : Page { public MainPage() { this.InitializeComponent(); // Support the mouse as well as pen (pen is active by default) for making notes. inkCanvas.InkPresenter.InputDeviceTypes = Windows.UI.Core.CoreInputDeviceTypes.Mouse | Windows.UI.Core.CoreInputDeviceTypes.Pen; // Request a nice small, sticky note sized grid to start. ApplicationView.PreferredLaunchViewSize = new Size { Height = 400, Width = 600 }; ApplicationView.PreferredLaunchWindowingMode = ApplicationViewWindowingMode.PreferredLaunchViewSize; ApplicationView.GetForCurrentView().SetPreferredMinSize(new Size { Height = 400, Width = 600 }); // Load any existing ink strokes saved by a previous instance. loadInk(); // Create a handler to save notes when the app is suspended. Application.Current.Suspending += new SuspendingEventHandler(OnSuspending); } /// /// The app is suspending, so save the current ink strokes. /// private async void OnSuspending(object sender, SuspendingEventArgs args) { SuspendingDeferral deferral = args.SuspendingOperation.GetDeferral(); await saveInk(); deferral.Complete(); } /// /// Load any ink data from file /// private async void loadInk() { Windows.Storage.StorageFolder storageFolder = Windows.Storage.ApplicationData.Current.LocalFolder; try { Windows.Storage.StorageFile file = await storageFolder.GetFileAsync("scratchpad.gif"); if (file != null) { // Open a file stream for reading. IRandomAccessStream stream = await file.OpenAsync(Windows.Storage.FileAccessMode.Read); // Read from file. using (var inputStream = stream.GetInputStreamAt(0)) { await inkCanvas.InkPresenter.StrokeContainer.LoadAsync(stream); } stream.Dispose(); } } catch (Exception e) { // Loading failed. } } /// /// Save ink data to a file. /// public async Task saveInk() { // Get all strokes on the InkCanvas. IReadOnlyList currentStrokes = inkCanvas.InkPresenter.StrokeContainer.GetStrokes(); // Strokes present on ink canvas. if (currentStrokes.Count > 0) { Windows.Storage.StorageFolder storageFolder = Windows.Storage.ApplicationData.Current.LocalFolder; Windows.Storage.StorageFile file = await storageFolder.CreateFileAsync("scratchpad.gif", Windows.Storage.CreationCollisionOption.ReplaceExisting); if (file != null) { // Prevent updates to the file until updates are // finalized with call to CompleteUpdatesAsync. Windows.Storage.CachedFileManager.DeferUpdates(file); // Open a file stream for writing. IRandomAccessStream stream = await file.OpenAsync(Windows.Storage.FileAccessMode.ReadWrite); // Write the ink strokes to the output stream. using (IOutputStream outputStream = stream.GetOutputStreamAt(0)) { await inkCanvas.InkPresenter.StrokeContainer.SaveAsync(outputStream); await outputStream.FlushAsync(); } stream.Dispose(); // Finalize write so other apps can update file. Windows.Storage.Provider.FileUpdateStatus status = await Windows.Storage.CachedFileManager.CompleteUpdatesAsync(file); if (status == Windows.Storage.Provider.FileUpdateStatus.Complete) { // File saved successfully } else { // Error saving file } } } } } }

* InkToolbar control
* Saving and loading files
* Pen and Stylus
* Handle app suspend