Search code examples
c#uwpuwp-xamlinkcanvaswin2d

Highlighter Ink Strokes Not Rendering onto Canvas Control


I'm trying to implement inking functionality using 'InkToolbar' & 'InkCanvas'. I want to be able to access the ink strokes as well, so I have activated custom drying on the InkCanvas as well.

Pen ink strokes & pencil ink strokes render properly on the canvas control but highlighter strokes are not rendering (not visible) on the canvas control.

I tried these solutions also but still the issue is there, Save Windows Ink as transparent PNG image - missing highlighter strokes

XAML Code

<Grid x:Name="container" Background="#FF282828">
    <Grid.RowDefinitions>
        <RowDefinition Height="40" />
        <RowDefinition Height="Auto" />
    </Grid.RowDefinitions>
    <InkToolbar x:Name="inkToolbar" InitialControls="None" Grid.Row="0">
        <InkToolbarBallpointPenButton />
        <InkToolbarPencilButton />
        <InkToolbarHighlighterButton />
        <InkToolbarEraserButton />
    </InkToolbar>
    <ScrollViewer ZoomMode="Enabled" Background="DarkGray" Grid.Row="1" x:Name="scrollViewer" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto" MinZoomFactor="0.5" Height="Auto">
        <Grid x:Name="viewport" VerticalAlignment="Top">
            <Border x:Name="viewportBorder" Background="White" BorderThickness="15, 15, 15, 15" BorderBrush="#FF353334" />
        </Grid>
    </ScrollViewer>
</Grid>

Code Behind

public sealed partial class MainPage : Page
{
    private Canvas SelectCanvas;
    private InkCanvas InkCanvas;
    private CanvasControl CanvasControl;
    private InkSynchronizer InkSynchronizer;
    private List<InkStrokeContainer> InkStrokeContainers = new List<InkStrokeContainer>();

    public MainPage()
    {
        this.InitializeComponent();
        InitViweport().GetAwaiter();
    }

    private async Task InitViweport()
    {
        FileOpenPicker openPicker = new FileOpenPicker
        {
            SuggestedStartLocation = PickerLocationId.PicturesLibrary,
            ViewMode = PickerViewMode.Thumbnail
        };

        // Filter to include a sample subset of file types.
        openPicker.FileTypeFilter.Clear();
        openPicker.FileTypeFilter.Add(".bmp");
        openPicker.FileTypeFilter.Add(".png");
        openPicker.FileTypeFilter.Add(".jpeg");
        openPicker.FileTypeFilter.Add(".jpg");

        var file = await openPicker.PickSingleFileAsync();

        if (file != null)
        {
            // Open a stream for the selected file.
            IRandomAccessStream fileStream = await file.OpenAsync(FileAccessMode.Read);

            var bitmapImage = new BitmapImage();
            bitmapImage.SetSource(fileStream);

            viewport.Width = bitmapImage.PixelWidth;
            viewport.Height = bitmapImage.PixelHeight;

            using (var stream = await file.OpenReadAsync())
            {
                WriteableBitmap bitmap = new WriteableBitmap(bitmapImage.PixelWidth, bitmapImage.PixelHeight);
                await bitmap.SetSourceAsync(stream);

                var image = new Image();
                image.Source = bitmap;

                viewport.Children.Add(image);
            }
        }

        AddCanvases();
    }

    private void AddCanvases()
    {
        SelectCanvas = new Canvas
        {
            Height = viewport.Height,
            Width = viewport.Width
        };

        InkCanvas = new InkCanvas
        {
            Width = viewport.Width,
            Height = viewport.Height
        };
        inkToolbar.TargetInkCanvas = InkCanvas;

        CanvasControl = new CanvasControl()
        {
            Height = viewport.Height,
            Width = viewport.Width,
            Background = new SolidColorBrush(Colors.Transparent)
        };
        CanvasControl.Draw += CanvasControlDraw;

        InkSynchronizer = InkCanvas.InkPresenter.ActivateCustomDrying();
        InkCanvas.InkPresenter.InputDeviceTypes = CoreInputDeviceTypes.Mouse | CoreInputDeviceTypes.Pen | CoreInputDeviceTypes.Touch;

        InkCanvas.InkPresenter.StrokesCollected += InkPresenter_StrokesCollected;

        Canvas.SetZIndex(InkCanvas, 5);
        Canvas.SetZIndex(SelectCanvas, 2);

        viewport.Children.Add(SelectCanvas);
        viewport.Children.Add(CanvasControl);
        viewport.Children.Add(InkCanvas);
    }

    private void CanvasControlDraw(CanvasControl sender, CanvasDrawEventArgs args) => DrawInk(args.DrawingSession);

    private void DrawInk(CanvasDrawingSession session)
    {
        foreach (var container in InkStrokeContainers)
        {
            var strokes = container.GetStrokes();

            using (var list = new CanvasCommandList(session))
            {
                using (var listSession = list.CreateDrawingSession())
                    listSession.DrawInk(strokes);

                using (var shadowEffect = new ShadowEffect { ShadowColor = Colors.DarkGray, Source = list })
                    session.DrawImage(shadowEffect, new Vector2(2, 2));
            }

            session.DrawInk(strokes);
        }
    }

    private void InkPresenter_StrokesCollected(InkPresenter sender, InkStrokesCollectedEventArgs args)
    {
        var strokeContainer = new InkStrokeContainer();
        strokeContainer.AddStrokes(from stroke in args.Strokes select stroke.Clone());
        InkStrokeContainers.Add(strokeContainer);
        InkSynchronizer.EndDry();

        CanvasControl.Invalidate();
    }
}

I'm not sure what the issue here. Could someone please point me in the right direction? Any help is much appreciated.

Thanks


Solution

  • I noticed that you are using InkSynchronizer.EndDry method, but not using InkSynchronizer.BeginDry method in your code. By testing, there is an System.InvalidOperationException occurs at the code InkSynchronizer.EndDry();. Then I comment the line of code and the app could run well just as you expected. And I also try to add the InkSynchronizer.BeginDry method before the InkSynchronizer.EndDry method, it also works. Therefore, you may need to delete the usage of InkSynchronizer.EndDry method, or add the usage of InkSynchronizer.BeginDry method in your app.