Back to Devexpress

Scrollbar Annotations

windowsforms-400723-controls-and-libraries-tree-list-feature-center-focus-selection-and-navigation-scrollbar-annotations.md

latest11.5 KB
Original Source

Scrollbar Annotations

  • Jun 01, 2025
  • 5 minutes to read

Scrollbar annotations help users locate specific nodes—such as those with validation errors, focus, or selection. The TreeList displays annotations as colored markers on the vertical scrollbar to visualize positions of corresponding nodes.

Run Demo: Scrollbar Annotations

Enable Scrollbar Annotations

Use the OptionsScrollAnnotations property to enable specific annotations:

  • ShowErrors - marks nodes with validation errors.

  • ShowFocusedRow - marks the focused node.

  • ShowSelectedRows - marks selected nodes.

  • ShowCustomAnnotations - marks nodes with custom annotations supplied via the CustomScrollAnnotation event.

  • C#

  • VB.NET

csharp
// Enable annotations for the focused row.
treeList.OptionsScrollAnnotations.ShowFocusedRow = DefaultBoolean.True;
vb
' Enable annotations for the focused row.
treeList.OptionsScrollAnnotations.ShowFocusedRow = DefaultBoolean.True

Note

The TreeList control also displays scrollbar annotations for search requests if the Find Panel operates in search mode.

Custom Annotations

Handle the CustomScrollAnnotation event to supply custom annotation data. Use the TreeListScrollAnnotationInfo class to specify annotation properties:

  • Node - the associated TreeList node.
  • Color - the color of the annotation.

Add annotation objects to the e.Annotations collection:

csharp
using DevExpress.XtraTreeList;

void treeList1_CustomScrollAnnotation(object sender, TreeListCustomScrollAnnotationsEventArgs e) {
    TreeListNode node = treeList1.FindNodeByFieldValue("DEPARTMENT", "Finance");
    e.Annotations = new List<TreeListScrollAnnotationInfo>();
    TreeListScrollAnnotationInfo info = new TreeListScrollAnnotationInfo() {
        Node = node,
        Color = Color.Orange
    };
    e.Annotations.Add(info);
}
vb
Private Sub treeList1_CustomScrollAnnotation(ByVal sender As Object, ByVal e As TreeListCustomScrollAnnotationsEventArgs) _
    Handles treeList1.CustomScrollAnnotation
    Dim node As TreeListNode = treeList1.FindNodeByFieldValue("DEPARTMENT", "Finance")
    e.Annotations = New List(Of TreeListScrollAnnotationInfo)()
    Dim info As New TreeListScrollAnnotationInfo() With {
        .Node = node,
        .Color = Color.Orange
    }
    e.Annotations.Add(info)
End Sub

Use the e.SetAnnotations method to apply annotations to a specific node. This method resets previous annotations.

csharp
using DevExpress.XtraTreeList;

void treeList1_CustomScrollAnnotation(object sender, TreeListCustomScrollAnnotationsEventArgs e) {
    TreeListNode node = treeList1.FindNodeByFieldValue("DEPARTMENT", "Finance");
    e.SetAnnotations(Color.Red, node);
}
vb
Private Sub treeList1_CustomScrollAnnotation(ByVal sender As Object, ByVal e As TreeListCustomScrollAnnotationsEventArgs) _
    Handles treeList1.CustomScrollAnnotation
    Dim node As TreeListNode = treeList1.FindNodeByFieldValue("DEPARTMENT", "Finance")
    e.SetAnnotations(Color.Red, node)
End Sub

Bookmarks with Scrollbar Annotations and Node Indicators

To implement user-defined bookmarks:

  1. Handle the TreeList.CustomScrollAnnotation event to associate nodes with annotations.
  2. Handle the TreeList.CustomDrawNodeIndicator event to draw bookmark glyphs.
  3. Handle the TreeList.ScrollAnnotationsStyle event to customize annotation appearance.
  4. Handle the KeyDown event to navigate between annotated nodes using TreeList.MoveToNextScrollAnnotation and TreeList.MoveToPrevScrollAnnotation methods.
csharp
using DevExpress.Utils;
using DevExpress.XtraEditors.Annotations;
using DevExpress.XtraTreeList.Menu;
using DevExpress.XtraTreeList.Nodes;
using DevExpress.XtraTreeList.Painter;

treeList1.CustomScrollAnnotation += OnCustomScrollAnnotation;
treeList1.CustomDrawNodeIndicator += OnCustomDrawRowIndicator;
treeList1.ScrollAnnotationsStyle += OnScrollAnnotationsStyle;
treeList1.KeyDown += OnKeyDown;

readonly HashSet<int> bookmarks = new HashSet<int>() { 5, 17, 74 };
// Set custom annotations.
void OnCustomScrollAnnotation(object sender, TreeListCustomScrollAnnotationsEventArgs e) {
    TreeListNode[] rowHandles = bookmarks.Select(x => treeList1.FindNodeByID(x)).ToArray();
    e.SetAnnotations(DevExpress.LookAndFeel.DXSkinColors.IconColors.Blue, rowHandles);
}
Utils.Design.ISvgPaletteProvider GetPalette() {
    return Utils.Svg.SvgPaletteHelper.GetSvgPalette(treeList1.LookAndFeel, Utils.Drawing.ObjectState.Normal);
}
void OnScrollAnnotationsStyle(object sender, TreeListScrollAnnotationsStyleEventArgs e) {
    var styleColor = ucScrollAnnotationsOptions.GetColor(e.Kind);
    if(!styleColor.IsEmpty)
        e.Color = styleColor;   
}
// Draw a bookmark glyph.
void OnCustomDrawRowIndicator(object sender, CustomDrawNodeIndicatorEventArgs e) {
    if(e.Node == null || treeList1.IsAutoFilterNode(e.Node))
        return;
    if(e.Info.ImageIndex == TreeListPainter.ErrorInNodeIndicatorImageIndex ||
    e.Info.ImageIndex == TreeListPainter.ErrorInFocusedNodeIndicatorImageIndex) {
        e.Info.ImageIndex = -1;
    }
    if(!ucScrollAnnotationsOptions.BookmarksEnabled || !bookmarks.Contains(e.Node.Id))
        return;
    e.DefaultDraw();
    var bookmarkImage = svgImageCollection.GetImage("bookmark", GetPalette(), ScaleDPI.ScaleSize(new Size(8, 8)));
    var imageBounds = PlacementHelper.Arrange(bookmarkImage.Size, e.Bounds, ContentAlignment.MiddleLeft);
    e.Cache.DrawImageUnscaled(bookmarkImage, imageBounds);
    e.Handled = true;
}
// Implement forward and back navigation.
void OnKeyDown(object sender, KeyEventArgs e) {
    if(e.KeyData == (Keys.F2 | Keys.Control) || e.KeyData == (Keys.B | Keys.Control))
        e.Handled = ToggleBookmark(treeList1.FocusedNode);
    if(e.KeyData == Keys.F2)
        e.Handled = treeList1.MoveToNextScrollAnnotation(ScrollAnnotationKind.Custom);
    if(e.KeyData == (Keys.F2 | Keys.Shift))
        e.Handled = treeList1.MoveToPrevScrollAnnotation(ScrollAnnotationKind.Custom);
}
bool ToggleBookmark(TreeListNode node) {
    int dataIndex = node != null ? node.Id : -1;
    if(dataIndex < 0)
        return false;
    if(bookmarks.Contains(dataIndex))
        bookmarks.Remove(dataIndex);
    else
        bookmarks.Add(dataIndex);
    treeList1.RefreshScrollAnnotations(ScrollAnnotationKind.Custom);
    treeList1.InvalidateNode(node);
    return true;
}
vb
Imports DevExpress.Utils
Imports DevExpress.XtraEditors.Annotations
Imports DevExpress.XtraTreeList.Menu
Imports DevExpress.XtraTreeList.Nodes
Imports DevExpress.XtraTreeList.Painter

Private ReadOnly bookmarks As New HashSet(Of Integer)() From {5, 17, 74}
' Set custom annotations.
Private Sub OnCustomScrollAnnotation(ByVal sender As Object, ByVal e As TreeListCustomScrollAnnotationsEventArgs) Handles treeList1.CustomScrollAnnotation
    Dim rowHandles() As TreeListNode = bookmarks.Select(Function(x) treeList1.FindNodeByID(x)).ToArray()
    e.SetAnnotations(DevExpress.LookAndFeel.DXSkinColors.IconColors.Blue, rowHandles)
End Sub
' Draw a bookmark glyph.
Private Sub OnCustomDrawRowIndicator(ByVal sender As Object, ByVal e As CustomDrawNodeIndicatorEventArgs) Handles treeList1.CustomDrawNodeIndicator
    If e.Node Is Nothing OrElse treeList1.IsAutoFilterNode(e.Node) Then
        Return
    End If
    If e.Info.ImageIndex = TreeListPainter.ErrorInNodeIndicatorImageIndex OrElse e.Info.ImageIndex = TreeListPainter.ErrorInFocusedNodeIndicatorImageIndex Then
        e.Info.ImageIndex = -1
    End If
    If (Not ucScrollAnnotationsOptions.BookmarksEnabled) OrElse (Not bookmarks.Contains(e.Node.Id)) Then
        Return
    End If
    e.DefaultDraw()
    Dim bookmarkImage = svgImageCollection.GetImage("bookmark", GetPalette(), ScaleDPI.ScaleSize(New Size(8, 8)))
    Dim imageBounds = PlacementHelper.Arrange(bookmarkImage.Size, e.Bounds, ContentAlignment.MiddleLeft)
    e.Cache.DrawImageUnscaled(bookmarkImage, imageBounds)
    e.Handled = True
End Sub
Private Function GetPalette() As Utils.Design.ISvgPaletteProvider
    Return Utils.Svg.SvgPaletteHelper.GetSvgPalette(treeList1.LookAndFeel, Utils.Drawing.ObjectState.Normal)
End Function
Private Sub OnScrollAnnotationsStyle(ByVal sender As Object, ByVal e As TreeListScrollAnnotationsStyleEventArgs) Handles treeList1.ScrollAnnotationsStyle
    Dim styleColor = ucScrollAnnotationsOptions.GetColor(e.Kind)
    If Not styleColor.IsEmpty Then
        e.Color = styleColor
    End If
End Sub
' Implement forward and back navigation.
Private Overloads Sub OnKeyDown(ByVal sender As Object, ByVal e As KeyEventArgs) Handles treeList1.KeyDown
    If e.KeyData = (Keys.F2 Or Keys.Control) OrElse e.KeyData = (Keys.B Or Keys.Control) Then
        e.Handled = ToggleBookmark(treeList1.FocusedNode)
    End If
    If e.KeyData = Keys.F2 Then
        e.Handled = treeList1.MoveToNextScrollAnnotation(ScrollAnnotationKind.Custom)
    End If
    If e.KeyData = (Keys.F2 Or Keys.Shift) Then
        e.Handled = treeList1.MoveToPrevScrollAnnotation(ScrollAnnotationKind.Custom)
    End If
End Sub
Private Function ToggleBookmark(ByVal node As TreeListNode) As Boolean
    Dim dataIndex As Integer = If(node IsNot Nothing, node.Id, -1)
    If dataIndex < 0 Then
        Return False
    End If
    If bookmarks.Contains(dataIndex) Then
        bookmarks.Remove(dataIndex)
    Else
        bookmarks.Add(dataIndex)
    End If
    treeList1.RefreshScrollAnnotations(ScrollAnnotationKind.Custom)
    treeList1.InvalidateNode(node)
    Return True
End Function

Specific Notes

See Also

Scrollbar Annotation Behavior