1

I have a problem when using multiple InkCanvas instances within an ItemsControl. Each InkCanvas instance receives an ImageBrush as Background and then is added to the ItemsControl. Basically this is a self written PdfViewer where users can draw remarks etc on the PdfPages and then save the file as pdf again. The original pdf is read, pages are converted to bitmaps and each bitmap is used as background for an InkCanvas.

This works well as long as I don´t click on another canvas. Here are two images to illustrate the problem:

Initial MouseClick

enter image description here

XAML of PdfViewer:

<UserControl x:Class="PdfTool.Code.Controls.PdfViewer"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:viewModel="clr-namespace:PdfTool.Code.ViewModel"
             mc:Ignorable="d" 
             d:DataContext="{d:DesignInstance {x:Type viewModel:CanvasViewModel}, IsDesignTimeCreatable=False}"
             d:DesignHeight="450" d:DesignWidth="800">
    <ScrollViewer PanningMode="Both" Background="{StaticResource MainBackgroundColor}">
        <ItemsControl x:Name="PagesContainer" MouseWheel="PagesContainer_OnMouseWheel">
            <ItemsControl.RenderTransform>
                <MatrixTransform/>
            </ItemsControl.RenderTransform>
        </ItemsControl>
    </ScrollViewer>
</UserControl>

Code behind:

public partial class PdfViewer : UserControl
{
    #region Bindable Properties
    ...
    #endregion

    public PdfViewer()
    {
        InitializeComponent();
    }

    private static async Task PdfToImages(PdfViewer pdfViewer, PdfDocument pdfDoc)
    {
        pdfViewer.Dispatcher.Invoke(() => pdfViewer.PagesContainer.Items.Clear());
        if (pdfDoc == null) return;

        for (uint i = 0; i < pdfDoc.PageCount; i++) {
            using (var page = pdfDoc.GetPage(i)) {
                await pdfViewer.Dispatcher.Invoke(async () =>
                {
                    var bitmap = await PageToBitmapAsync(page);
                    pdfViewer.PagesContainer.Items.Add(new PdfDetailElement(new ImageBrush(bitmap) {Stretch = Stretch.Uniform}));
                });
            }
        }
    }

    ...
}

XAML of a Single PdfPage:

<UserControl x:Class="PdfTool.Code.Controls.PdfDetailElement"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:viewModel="clr-namespace:PdfTool.Code.ViewModel"
             mc:Ignorable="d" 
             d:DataContext="{d:DesignInstance {x:Type viewModel:CanvasViewModel}, IsDesignTimeCreatable=False}"
             d:DesignHeight="450" d:DesignWidth="800">
    <Grid Margin="10,10,5,10">
        <Viewbox Stretch="Fill">
            <InkCanvas x:Name="inkCanvas" 
                       Background="{x:Null}" 
                       DefaultDrawingAttributes="{Binding SelectedDrawingAttributes}" 
                       EditingMode="{Binding SelectedEditingMode, UpdateSourceTrigger=PropertyChanged}"/>
        </Viewbox>
    </Grid>
</UserControl>

Code behind:

public partial class PdfDetailElement : UserControl
{
    public PdfDetailElement()
    {
        InitializeComponent();
    }

    public PdfDetailElement(ImageBrush image)
    {
        DataContext = Gui.Main.CanvasSettings;
        InitializeComponent();
        inkCanvas.Background = image;
        inkCanvas.Width = image.ImageSource.Width;
        inkCanvas.Height = image.ImageSource.Height;
    }
}

Only the MouseWheel event is used for Zooming and it is not the cause of the issue. It seems, the coordinates of the mouse on InkCanvas 1 are used as initial value for InkCanvas 2. How can I change this behaviour?

Chris
  • 835
  • 12
  • 27
  • It's difficult without knowing implementation details. Why do you think cooridnates of previous page are used for the next? It doesn't add value if the user can draw lines between the pages. Why don't you isolate the pages as individual objects? Each page objects would have it's own context data like mouse click coordinates and drawn objects or annotations. Each page would handle mouse events individually using relative coordinates. – BionicCode Apr 01 '20 at 11:07
  • Like in your first image: clicking the top of the next page is actually a click on the bottom of the view port. You have to translate this bottom view port coordinate e.g. Y = 800 to the next page's top coordinate e.g. Y = 50. Try to use `Mouse.GetPosition(pageCanvas)` to get coordinates relative to the selected page canvas instead of the viewport. Also cancel previous page's mouse actions, when the pointer is leaving the context or page canvas. – BionicCode Apr 01 '20 at 11:07
  • Hi, and thanks for answering :-) To answer your first comment: My pages are isolated as individual objects, see the line -> pdfViewer.PagesContainer.Items.Add(new PdfDetailElement(new ImageBrush(bitmap) {Stretch = Stretch.Uniform})); Each canvas is a separate element and also handles MouseEvents individually, but currently I haven´t implemented any. – Chris Apr 01 '20 at 13:45
  • To answer the second question: For testing purposes I implemented a PreviewMouseDown event on the InkCanves control and it returns the correct mouse coordinates, but does not draw the stroke at the right place. Well, it starts of at the right place, but then jumps down as described original question above. That causes a line to be drawn between to Points - the correect starting Point and the Point it jumps to. – Chris Apr 01 '20 at 13:48
  • It seems the Scrollviewer automatically scrolls down to center the element the focus is set on by starting to draw a line? – Chris Apr 01 '20 at 13:49
  • @BionicCode, thanks for trying to help. I´ve found a solution now, see my answer below. – Chris Apr 01 '20 at 14:00

1 Answers1

0

I got it: Stop WPF ScrollViewer automatically scrolling to perceived content got me in the right direction. I simply had to set

Focusable="False"

on the InkCanvas and my problem is solved, as the RequestBringIntoView event of the Scrollbar is not fired if the object to bring into view is not focusable...

Chris
  • 835
  • 12
  • 27