This is Part 8 of my "VB Win10 Apps" series. (Everything applies to C# as well, of course).
- Part 1: getting started
- Part 2: issues with common libraries - JSON.Net, SignalR, SharpDX, SQLite, LiveSDK
- Part 3: design-time data in XAML
- Part 4: references
- Part 5: projects, targets, libraries
- Part 6: porting from 8.1 universal to Win10
- Part 7: porting from Phone8.1 to Phone8.1+Win10 using Shared Projects
- > Part 8: getting started with win2d accelerated graphics
- Part ?: ... (please let me know what you'd like me to write about next!)
It's time for some fun graphics! Here are three small apps I wrote using the win2d accelerated-graphics library.
- Watch live coding screencast: creating a simple win2d graphics app from scratch [18mins]
- Full source code for all three apps is here: https://github.com/ljw1004/blog/tree/master/Win2d
- Official website for win2d: http://microsoft.github.io/Win2D/html/Introduction.htm
- My "explosions" is just a cut-down version of the official win2d "particle-system" sample.
Transcript of key points in the screencast:
[0:40] To get started, References > Managed Nuget References > add a reference to "Win2d"
[1:10] Inside your MainPage.xaml, add these two lines. Unfortunately in VS2015 RC the XAML designer just shows an error instead of the CanvasControl. This will be fixed in VS2015 RTM.
<canvas:CanvasControl x:Name="canvas1" ClearColor="Black"/>
[2:05] Win2d naturally works with System.Numerics.Vector and Matrix types, and also with single-width floating points ("Single" in VB, "float" in C#). This can get a bit awkward because most of the .NET framework is more at home with "Double". You'll have to figure out the cleanest dividing line between the two. In the app we're writing, the cleanest point was the random number generator.
[4:05] There's a neat trick to do animation. We override the Draw method, and at the end of it call "canvas1.Invalidate". This causes our Canvas_Draw method to be invoked once a frame, 60 frames a second. This makes it a handy place to update the animation as well as draw stuff.
Sub Draw(sender As CanvasControl, e As CanvasDrawEventArgs) Handles canvas1.Draw
e.DrawingSession.DrawLine(pos(1), pos(2), Colors.Azure)
Pro tip: the CanvasAnimatedControl is specifically designed for animations such as these. It specifically has an Update event which it fires on a background thread so as not to hinder responsiveness of the UI thread.
Pro tip: if you want all your drawing commands to be transformed by a view matrix - for scaling, rotation, offsets or others - use the CanvasDrawingSession.Transform property.
[6:15] There's a useful feature in VB called "Static Dim". It's similar to "static" in C++, but unrelated to "static" in C#. You can declare a local variable to be a "Static Dim". Its initializer is run only once, the first time the method is invoked. All subsequent invocations re-use the existing variable. (Under the hood, the compiler basically promotes it to be a field of the containing type, with appropriate thread-safe initialization).
Function Rnd(min As Double, max As Double) As Single
Static Dim r As New Random
Return CSng(min + (max - min) * r.NextDouble())
[8:15] How to do to bitmaps? The win2d type is called Microsoft.Graphics.Canvas.CanvasBitmap. You have to load it (along with all your other win2d resource initialization) in response to CanvasControl.CreateResources event. The deal is that the graphics accelerator might be reset (such as remote desktop, screen sharing, mirrorcast), and all your resources lost, so you have to cope with the possibility that Win2d will call your CreateResources handler off its own accord to re-establish those resources: your handler has to be idempotent, a fancy word for "no harm if it's called subsequent times".
Pro tip: in the video I set the ball's velocities inside CreateResources. That's not the best place, since things like screen sharing will make the ball's velocities get reset. It would have been better to leave them set in OnNavigatedTo.
[11:30] There's a particular technique that Win2d uses if your CreateResources handler is asynchronous (e.g. for loading bitmaps): the asynchronous work you do has to be wrapped up in a call to CanvasCreateResourcesEventArgs.TrackAsyncAction. Here's an example...
Sub CreateResources(sender As CanvasControl, e As CanvasCreateResourcesEventArgs) Handles canvas1.CreateResources
Dim loadTask =
bitmap = Await CanvasBitmap.LoadAsync(canvas1, "explosion.png")
[14:30] Please remember to check that your app works on Mobile emulator and also in Release build.
[14:50] When your UWP app runs on mobile, the size of its MainPage (and other pages) doesn't normally include the "Status Bar" area at the top (with wifi strength indicator, battery indicator and clock). For these graphics apps, what we'll do is extend our MainPage to include the full size of the mobile screen including that Status-Bar area, and we'll leave the indicators to be superimposed on top of our graphics. Here's the magic code to do that, as the first line of your App.OnLaunched event handler: