0

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

  • I'm guessing that it might have something to do with the drawing surface background which the highlighter ink is drawn on, tried setting the canvas control background as the same as the image using image brush `Background = new ImageBrush { ImageSource = image.Source }` but still no luck. – Madushan Amarasinghe Mar 24 '21 at 11:08
  • One workaround I found was setting the clear color of the canvas control like `CanvasControl = new CanvasControl() { Height = viewport.Height, Width = viewport.Width, ClearColor = Color.FromArgb(100, 255, 255, 255) };` but it draws a faded layer on top of the content which looks a bit out of place. Is there no other way of doing this? – Madushan Amarasinghe Mar 24 '21 at 11:26
  • I have tested the code provided by you. There is an System.InvalidOperationException occurs at the code `InkSynchronizer.EndDry();`. I comment the line of code and then the app could run well. In my test, the highliter ink could render on the selected image. I’m confused with the sentence “but highlighter strokes are not rendering (not visible) on the canvas control”. Could you please describe your expected behavior in detail? – YanGu Mar 25 '21 at 07:23
  • Funny thing, I put the code which was only related to the ink drawing scenario in the question & have missed out to put `InkSynchronizer.BeginDry();` in the question. I commented the EndDry line as you have done & it resulted in the behavior I wanted. I guess not using the InkSynchronizer fixed the issue. Can you post an answer so I can mark it as fixed? @YanGu-MSFT – Madushan Amarasinghe Mar 25 '21 at 09:42

1 Answers1

0

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.

YanGu
  • 3,006
  • 1
  • 3
  • 7