Как поместить изображение "метки места" внутри полосы прокрутки в WPF?

У меня есть длинный инструмент просмотра прокрутки, и я хочу отметить важные пятна с маленькими изображениями на полосе прокрутки. Если щелкнуть изображение, полоса прокрутки переместится к соответствующему контенту.

Я видел эту функциональность в нескольких приложениях, таких как Eclipse и Chrome, и задавался вопросом, как воспроизвести ее с помощью WPF.

Ответ 1

Краткий ответ: "Измените шаблон ScrollBar".

Длинный ответ... Я бы добавил ItemsControl в шаблон элемента управления ScrollBar. Я бы поставил этот ItemsControl поверх шаблона с его IsHitTestVisible, установленным в false, чтобы он не захватывал события мыши.

Тогда я бы использовал Canvas в качестве ItemsPanelTemplate, чтобы иметь возможность правильно размещать пятна. Я бы использовал привязку данных со свойством ItemsSource ItemsControl и DataTemplate, чтобы визуализировать каждый элемент с изображением.

Вот пример, который я сделал, используя Blend. Конечно, он не завершен (например, он не обрабатывает события мыши), но я надеюсь, что он станет отправной точкой для вас.

alt text
(источник: japf.fr)

<ControlTemplate TargetType="{x:Type ScrollBar}">
    <Grid SnapsToDevicePixels="true" Background="{TemplateBinding Background}">
        <Grid.ColumnDefinitions>
            <ColumnDefinition MaxWidth="{DynamicResource {x:Static SystemParameters.HorizontalScrollBarButtonWidthKey}}"/>
            <ColumnDefinition Width="0.00001*"/>
            <ColumnDefinition MaxWidth="{DynamicResource {x:Static SystemParameters.HorizontalScrollBarButtonWidthKey}}"/>
        </Grid.ColumnDefinitions>
        <RepeatButton Style="{StaticResource ScrollBarButton}" Command="{x:Static ScrollBar.LineLeftCommand}" Microsoft_Windows_Themes:ScrollChrome.ScrollGlyph="LeftArrow"/>
        <Track x:Name="PART_Track" Grid.Column="1" d:IsHidden="True">
            <Track.Thumb>
                <Thumb Style="{StaticResource ScrollBarThumb}" Microsoft_Windows_Themes:ScrollChrome.ScrollGlyph="HorizontalGripper"/>
            </Track.Thumb>
            <Track.IncreaseRepeatButton>
                <RepeatButton Style="{StaticResource HorizontalScrollBarPageButton}" Command="{x:Static ScrollBar.PageRightCommand}"/>
            </Track.IncreaseRepeatButton>
            <Track.DecreaseRepeatButton>
                <RepeatButton Style="{StaticResource HorizontalScrollBarPageButton}" Command="{x:Static ScrollBar.PageLeftCommand}"/>
            </Track.DecreaseRepeatButton>
        </Track>
        <ItemsControl Grid.Column="1" HorizontalAlignment="Stretch">
            <sys:Double>10</sys:Double>
            <sys:Double>50</sys:Double>
            <sys:Double>100</sys:Double>
            <sys:Double>140</sys:Double>
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <Rectangle Fill="Orange" Width="3" Height="16"/>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
            <ItemsControl.ItemContainerStyle>
                <Style TargetType="ContentPresenter">
                    <Setter Property="Canvas.Left" Value="{Binding }" />
                </Style>
                                        </ItemsControl.ItemContainerStyle>
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <Canvas/>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
        </ItemsControl>
        <RepeatButton Style="{StaticResource ScrollBarButton}" Grid.Column="2" Command="{x:Static ScrollBar.LineRightCommand}" Microsoft_Windows_Themes:ScrollChrome.ScrollGlyph="RightArrow" d:IsHidden="True"/>
    </Grid>
</ControlTemplate>

Ответ 2

Чтобы помочь japfs ответить: Я решил обновить информацию об изменении размера: Вы можете использовать стиль japfs и применить ItemSource к ItemControl:

ItemsSource="{Binding Positions, UpdateSourceTrigger=PropertyChanged}"

Просто убедитесь, что Positions имеет тип ObservableCollection, и позиции пересчитываются в событие SizeChanged. Кроме того, в этом вызове событий (интерфейс INotifyPropertyChanged, который должен реализовывать ваш ViewModel)

OnPropertyChanged("Positions");

Сначала пробовал его с помощью списка, но это неправильно обновлялось. Работал с ObservableCollection просто отлично.