Search code examples
c#wpfxamlwpf-controlsinkcanvas

Stop WPF InkBrush from causing a scroll event


The Application

I am working on an EHR application. The page I am working on allows for users to draw on a patient's notes using an InkCanvas and InkBrush. When the users use the mouse to draw, everything works perfectly. However, when they use a touch screen to draw the scrollbar is being manipulated as well. This causes the page to scroll as they are drawing.

What I've Tried

So far I've tried to handle touch events such as TouchDown, TouchUp, and TouchMove mainly using e.handled = true.

Application Code (Sorry, a lot of code!)

ScribblePad.xaml (snippet of Border + InkCanvas code)

<Border Grid.Column="1"
                Margin="1,1,8,8"
                Background="White"
                BorderBrush="Black"
                BorderThickness="1"
                Cinch:SingleEventCommand.RoutedEventName="MouseDown"
                Cinch:SingleEventCommand.TheCommandToRun="{Binding 
AddTextCommand}"
                RequestBringIntoView="Border_RequestBringIntoView">
          <Border.Effect>
            <DropShadowEffect />
          </Border.Effect>

          <InkCanvas x:Name="NewScribbles"
                     Width="{Binding Parent.PageWidth}"
                     Height="{Binding Parent.PageHeight}"
                     DefaultDrawingAttributes="{Binding Parent.SelectedInkBrush}"
                     EditingMode="{Binding Parent.InkEditMode}"
                     Strokes="{Binding NewStrokes}">
            <Grid>
              <Grid.Style>
                <Style TargetType="{x:Type Grid}">
                  <Style.Triggers>
                    <DataTrigger Binding="{Binding Parent.IsTextMode}"
                                 Value="True">
                      <Setter Property="Cursor"
                              Value="IBeam" />
                    </DataTrigger>
                  </Style.Triggers>
                </Style>
              </Grid.Style>
              <ContentPresenter Width="{Binding Parent.PageWidth}"
                                Height="{Binding Parent.PageHeight}"
                                Content="{Binding WrappedVisual}" />
              <ItemsControl x:Name="PastScribbles"
                            ItemsSource="{Binding ScribbleData}">
                <ItemsControl.ItemsPanel>
                  <ItemsPanelTemplate>
                    <Grid />
                  </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>
                <ItemsControl.ItemTemplate>
                  <DataTemplate>
                    <Grid Width="{Binding DataContext.PageWidth, RelativeSource={RelativeSource AncestorType={x:Type UserControl}, Mode=FindAncestor}}"
                          Height="{Binding DataContext.PageHeight, RelativeSource={RelativeSource AncestorType={x:Type UserControl}, Mode=FindAncestor}}"
                          Visibility="{Binding IsVisible, Converter={StaticResource BoolToVisibilityConverter}}">
                      <InkPresenter Strokes="{Binding Strokes}"
                                    Visibility="{Binding InkVisibility}">
                        <InkPresenter.Effect>
                          <DropShadowEffect Opacity="{Binding DropShadowOpacity}"
                                            ShadowDepth="2" />
                        </InkPresenter.Effect>
                      </InkPresenter>

                      <ItemsControl Width="{Binding DataContext.PageWidth, RelativeSource={RelativeSource AncestorType={x:Type UserControl}, Mode=FindAncestor}}"
                                    Height="{Binding DataContext.PageHeight, RelativeSource={RelativeSource AncestorType={x:Type UserControl}, Mode=FindAncestor}}"
                                    ItemsSource="{Binding TextData}"
                                    Visibility="{Binding TextVisibility}">
                        <ItemsControl.ItemsPanel>
                          <ItemsPanelTemplate>
                            <Canvas />
                          </ItemsPanelTemplate>
                        </ItemsControl.ItemsPanel>
                        <ItemsControl.ItemTemplate>
                          <DataTemplate>
                            <TextBlock Margin="4"
                                       FontSize="{Binding TextData.FontSize}"
                                       FontWeight="{Binding FontWeight}"
                                       Text="{Binding TextData.Text, FallbackValue=This is a placeholder}"
                                       TextWrapping="Wrap"
                                       MaxWidth="{Binding TextData.MaxWidth}">
                              <TextBlock.Foreground>
                                <SolidColorBrush Color="{Binding TextData.TextColor, FallbackValue=Black}" />
                              </TextBlock.Foreground>
                            </TextBlock>
                          </DataTemplate>
                        </ItemsControl.ItemTemplate>
                        <ItemsControl.ItemContainerStyle>
                          <Style>
                            <Setter Property="Canvas.Left"
                                    Value="{Binding TextData.LeftPosition}" />
                            <Setter Property="Canvas.Top"
                                    Value="{Binding TextData.TopPosition}" />
                          </Style>
                        </ItemsControl.ItemContainerStyle>
                        <ItemsControl.Effect>
                          <DropShadowEffect Opacity="{Binding DropShadowOpacity}"
                                            ShadowDepth="2" />
                        </ItemsControl.Effect>
                      </ItemsControl>

                    </Grid>
                  </DataTemplate>
                </ItemsControl.ItemTemplate>
              </ItemsControl>
            </Grid>
          </InkCanvas>
        </Border>

ScribblePad.cs

  /// <summary>
  /// Interaction logic for ScribblePadUC.xaml
  /// </summary>
  public partial class ScribblePadUC : UserControl
  {
    /// <summary>
    /// Initializes a new instance of the <see cref="ScribblePadUC"/> class.
    /// </summary>
    public ScribblePadUC()
    {
      this.InitializeComponent();
    }

    /// <summary>
    /// Handles the RequestBringIntoView event of the Border control.
    /// </summary>
    /// <param name="sender">The source of the event.</param>
    /// <param name="e">The <see cref="System.Windows.RequestBringIntoViewEventArgs"/> instance containing the event data.</param>
    private void Border_RequestBringIntoView(object sender, RequestBringIntoViewEventArgs e)
    {
      // Don't want auto-scrolling when trying to scribble... causes annoying vertical line
      e.Handled = true;
    }

    /// <summary>
    /// Handles the Loaded event of the TextBox control.
    /// </summary>
    /// <param name="sender">The source of the event.</param>
    /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
    private void TextBox_Loaded(object sender, RoutedEventArgs e)
    {
      ((TextBox)sender).Focus();
    }

    /// <summary>
    /// Event handler for the PreviewKeyDown event
    /// </summary>
    /// <param name="sender">The sender of the event</param>
    /// <param name="e">The event arguments</param>
    private void UserControl_PreviewKeyDown(object sender, KeyEventArgs e)
    {
      bool openAddendem = false;

      if (Keyboard.IsKeyDown(Key.A) && (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl)))
      {
        openAddendem = true;
      }

      if (openAddendem == true)
      {
        ScribblePadVM vm = this.DataContext as ScribblePadVM;

        if (vm != null)
        {
          if (vm.AddendumWidth.Value == 0)
          {
            vm.AddendumOpenCloseCommand.Execute(null);
          }

          vm.AddAddendumCommand.Execute(true);

          e.Handled = true;
        }
      }
    }

    /// <summary>
    /// Event handler for the loaded event
    /// </summary>
    /// <param name="sender">The sender of the event</param>
    /// <param name="e">The event arguments</param>
    private void UserControl_Loaded(object sender, RoutedEventArgs e)
    {
      Window window = this.TryFindVisualParent<Window>();

      if (window != null && window is HealthRecord.HealthRecordWindow)
      {
        window.PreviewKeyDown += (s, args) =>
        {
          this.UserControl_PreviewKeyDown(s, args);
        };
      }
    }
  }
}

I intentionally left the ViewModel.cs out as it most likely does not pertain to this problem.

What I want to happen

When I use a touch screen, the scrollbar does not get controlled if I am trying to draw on the canvas.

Thanks for reading.


Solution

  • PreviewTouchDown allows you to handle the touch event before it bubbles to the corresponding UI Element. The same goes for PreviewMouseDown.

    (putting official answer so question can be marked solved)