Categories
Code Surface Windows 10

Building an Etch-a-Sketch with Surface Dial

Microsoft have demonstrated how the Surface Dial can be used in conjunction with Pen input for drawing, but what about drawing with the Dial itself?

For those of us with warm memories of the Etch-a-Sketch I thought it would be fun to replicate the experience with the Surface Dial. Now of course there is a big caveat – you only have a single Dial to use at a time. This can be worked around by using the Click functionality of the Dial – Tapping the top of the dial switches between horizontal and vertical drawing.

The code to achieve this is very simple, and I’ve pasted it in a Gist here:-

<Page
x:Class="DialASketch.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:DialASketch"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Page.TopAppBar>
<CommandBar>
<CommandBar.PrimaryCommands>
<AppBarButton Icon="Clear" Label="Clear" Click="ClearButton_Click"/>
</CommandBar.PrimaryCommands>
</CommandBar>
</Page.TopAppBar>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<InkCanvas x:Name="SketchCanvas" />
<Canvas>
<Ellipse x:Name="Pointer" Width="4" Height="4" Fill="Black"/>
</Canvas>
</Grid>
</Page>

view raw
MainPage.xaml
hosted with ❤ by GitHub

using System;
using System.Collections.Generic;
using Windows.Foundation;
using Windows.UI;
using Windows.UI.Input;
using Windows.UI.Input.Inking;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
namespace DialASketch
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class MainPage : Page
{
private RadialController Controller;
private RadialControllerMenuItem MenuItem;
private bool isHorizontal = true;
InkStrokeBuilder b = new InkStrokeBuilder();
private Point lastPoint;
public MainPage()
{
this.InitializeComponent();
SketchCanvas.SizeChanged += SketchCanvas_SizeChanged;
Controller = RadialController.CreateForCurrentView();
Controller.RotationResolutionInDegrees = 2;
Controller.RotationChanged += Controller_RotationChanged;
Controller.ButtonClicked += Controller_ButtonClicked;
MenuItem = RadialControllerMenuItem.CreateFromKnownIcon("DialASketch", RadialControllerMenuKnownIcon.PenType);
Controller.Menu.Items.Add(MenuItem);
}
private void SketchCanvas_SizeChanged(object sender, SizeChangedEventArgs e)
{
if (e.NewSize.Width > 0)
{
if (lastPoint.X == 0f)
{
lastPoint = new Windows.Foundation.Point { X = SketchCanvas.ActualWidth / 2, Y = SketchCanvas.ActualHeight / 2 };
Canvas.SetLeft(Pointer, lastPoint.X 2);
Canvas.SetTop(Pointer, lastPoint.Y 2);
}
}
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
Controller?.Menu.Items.Clear();
base.OnNavigatedFrom(e);
}
private void Controller_ButtonClicked(RadialController sender, RadialControllerButtonClickedEventArgs args)
{
// switch direction (since we only have one dial)
isHorizontal = !isHorizontal;
}
private void Controller_RotationChanged(RadialController sender, RadialControllerRotationChangedEventArgs args)
{
var delta = args.RotationDeltaInDegrees * 4;
Point newPoint = new Windows.Foundation.Point { X = isHorizontal ? Math.Min(Math.Max(lastPoint.X + delta, 0), SketchCanvas.ActualWidth) : lastPoint.X, Y = !isHorizontal ? Math.Min(Math.Max(lastPoint.Y delta, 0), SketchCanvas.ActualHeight) : lastPoint.Y };
Canvas.SetLeft(Pointer, newPoint.X 2);
Canvas.SetTop(Pointer, newPoint.Y 2);
var stroke = b.CreateStroke(new List<Point> { lastPoint, newPoint });
lastPoint = newPoint;
stroke.DrawingAttributes.Color = Colors.Black;
stroke.DrawingAttributes.PenTip = PenTipShape.Circle;
SketchCanvas.InkPresenter.StrokeContainer.AddStroke(stroke);
}
private void ClearButton_Click(object sender, RoutedEventArgs e)
{
SketchCanvas.InkPresenter.StrokeContainer.Clear();
lastPoint = new Windows.Foundation.Point { X = SketchCanvas.ActualWidth / 2, Y = SketchCanvas.ActualHeight / 2 };
Canvas.SetLeft(Pointer, lastPoint.X 2);
Canvas.SetTop(Pointer, lastPoint.Y 2);
}
}
}

view raw
MainPage.xaml.cs
hosted with ❤ by GitHub

You could extend this to support keyboard input too as at the moment the app will do nothing if you don’t have a Dial setup on the PC. The output is created using the InkCanvas control along with a small Ellipse to show the current location:-

Dial-A-Sketch

By Peter Freeman Foot

Microsoft Windows Development MVP