Olivier Giniaux
Olivier Giniaux

Reputation: 950

How to make an annotated scrollbar?

I am using a DataGrid to display some information.

I would like to have a Annotated ScrollBar for this DataGrid (to highlight lines, like in VS)

I have this ControlTemplate in my App.xaml. I added a rectangle in "AnnotationCanvas" as an example, but the idea is to fill this canvas automatically.

However, I can't get a reference to this canvas.

I tried dataGrid.FindName("AnnotationCanvas") and dataGrid.FindResource("AnnotationCanvas"), but with no success.

How could I do this ? Thanks in advance.

<ControlTemplate x:Key="VerticalScrollBar" TargetType="{x:Type ScrollBar}">
  <Grid x:Name="grid">
    <Grid.RowDefinitions>
      <RowDefinition MaxHeight="18" />
      <RowDefinition Height="0.00001*" />
      <RowDefinition MaxHeight="18" />
    </Grid.RowDefinitions>
    <Border Grid.RowSpan="3"
            CornerRadius="2"
            Background="#F0F0F0" />

    <Canvas x:Name="AnnotationCanvas"
            Grid.Row="1">
      <!--ANNOTATIONS GO HERE-->
      <Rectangle Width="3"
                 Height="30"
                 Fill="Red" />
    </Canvas>

    <RepeatButton Grid.Row="0"
                  Style="{StaticResource ScrollBarLineButton}"
                  Height="18"
                  Command="ScrollBar.LineUpCommand"
                  Content="M 0 4 L 8 4 L 4 0 Z" />
    <Track Name="PART_Track"
           Grid.Row="1"
           IsDirectionReversed="true">
      <Track.DecreaseRepeatButton>
        <RepeatButton Style="{StaticResource ScrollBarPageButton}"
                      Command="ScrollBar.PageUpCommand" />
      </Track.DecreaseRepeatButton>
      <Track.Thumb>
        <Thumb Style="{StaticResource ScrollBarThumb}"
               Margin="1,0,1,0"
               Background="{StaticResource HorizontalNormalBrush}"
               BorderBrush="{StaticResource HorizontalNormalBorderBrush}" />
      </Track.Thumb>
      <Track.IncreaseRepeatButton>
        <RepeatButton Style="{StaticResource ScrollBarPageButton}" 
                      Command="ScrollBar.PageDownCommand" />
      </Track.IncreaseRepeatButton>
    </Track>
    <RepeatButton Grid.Row="3"
                  Style="{StaticResource ScrollBarLineButton}"
                  Height="18"
                  Command="ScrollBar.LineDownCommand"
                  Content="M 0 0 L 4 4 L 8 0 Z" />
  </Grid>
</ControlTemplate>

Upvotes: 4

Views: 831

Answers (1)

Olivier Giniaux
Olivier Giniaux

Reputation: 950

I found out that the VisualTreeHelper I was using to get to the ScrollBar from the DataGrid won't work it the Control is not completely loaded. InitializeComponent() was not enough, I had to wait for Loaded event to trigger.

Here is my code (working) :

private Canvas annotationCanvas;
private void Loaded(object sender, RoutedEventArgs e) {

    ScrollBar scrollBar = GetVisualChild<ScrollBar>(table);
    annotationCanvas = (Canvas)scrollBar.Template.FindName("AnnotationCanvas", scrollBar);

    UpdateAnnotations();
}

private static T GetVisualChild<T>(DependencyObject parent) where T : Visual {
    T child = default(T);

    int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
    for (int i = 0; i < numVisuals; i++) {
        Visual v = (Visual)VisualTreeHelper.GetChild(parent, i);
        child = v as T;
        if (child == null) {
            child = GetVisualChild<T>(v);
        }
        if (child != null) {
            break;
        }
    }
    return child;
}

// Fill the Canvas with horizontal markers. Can be optimized.
private void UpdateAnnotations() {
    if (annotationCanvas == null)
        return;

    annotationCanvas.Children.Clear();

    int i = 0;
    double m = items.Count;
    double height = table.ActualHeight;

    foreach (Item item in items) {
        if (item.someCondition) {
            int p = (int)(height * i / m);
            annotationCanvas.Children.Add(new Line() { X1 = 0, Y1 = p, X2 = 30, Y2 = p, StrokeThickness = 1, Stroke = Brushes.Red });
        }
        i++;
    }
}

Upvotes: 3

Related Questions