Win2D provides the ability to use GPU-accelerated 2D graphics in WinUI 3 apps.
The official documentaion positions Win2D as a solution for simple games, charts and other simple 2D graphics. I use it for the image preview area of my small pet project (annonce will come soon).
To use Win2D in WinUI 3 projects Microsoft.Graphics.Win2D package is used. API Reference is under reconstruction.
The simplest way to start using Win2D is to place CanvasControl
and add handlers for Draw
and CreateResources
events. The main trick is to capture the load task to control resource loading process:
xmlns:win2d="using:Microsoft.Graphics.Canvas.UI.Xaml"
<win2d:CanvasControl CreateResources="CanvasControl_CreateResources" Draw="CanvasControl_Draw"/>
private Task? imageLoadTask;
private CanvasBitmap? image;
// Draw an image
private void CanvasControl_Draw(CanvasControl sender, CanvasDrawEventArgs args)
{
// Wait for resource loading
if (!IsImageLoading() && image != null)
{
// Calculate the factor to fit an image into the canvas
var factor = Math.Min(sender.ActualWidth / image.Size.Width, sender.ActualHeight / image.Size.Height);
// Calculate a new image size
var width = factor * image.Size.Width;
var height = factor * image.Size.Height;
// Calculate an image center
var x = (sender.ActualWidth - width) / 2;
var y = (sender.ActualHeight - height) / 2;
// Draw an image
args.DrawingSession.DrawImage(
image,
new Rect(x, y, width, height),
new Rect(0, 0, image.Size.Width, image.Size.Width),
1,
CanvasImageInterpolation.NearestNeighbor);
}
}
// Creates resources needed for drawing
private void CanvasControl_CreateResources(CanvasControl sender, CanvasCreateResourcesEventArgs args)
{
args.TrackAsyncAction(CreateResources().AsAsyncAction());
}
// Creates resources
private async Task CreateResources()
{
// Cancel the current loading task
await CancelTask();
// Dispose the current image
image?.Dispose();
image = null;
// Load a new image from ImageStream stream
await LoadImage(ImageStream?.AsRandomAccessStream());
}
// Cancels the current loading
private async Task CancelTask()
{
if (imageLoadTask != null)
{
imageLoadTask.AsAsyncAction().Cancel();
try
{
await imageLoadTask;
}
catch
{ }
imageLoadTask = null;
}
}
// Loads a new image
private async Task LoadImage(IRandomAccessStream? stream)
{
// Loads an image asynchronously
if (stream != null)
{
image = await CanvasBitmap.LoadAsync(ImageCanvas, stream);
}
else
{
image = null;
}
// Update a canvas
ImageCanvas.Invalidate();
}
// Waits for an image loading
private bool IsImageLoading()
{
if (imageLoadTask == null)
{
return false;
}
else if (!imageLoadTask.IsCompleted)
{
return true;
}
else
{
try
{
imageLoadTask.Wait();
}
catch (AggregateException ex)
{
ex.Handle(x => throw x);
}
finally
{
imageLoadTask = null;
}
return false;
}
}
When the page or other container is unloaded we need to dispose the canvas properly:
canvas.RemoveFromVisualTree();
canvas = null;
I’ll tell you more about Win2D using my project as an example. Stay with us.