Home > Back-end >  Nested ScrollViewer ScrollChanged in ControlTemplate
Nested ScrollViewer ScrollChanged in ControlTemplate

Time:09-23

I have a ScrollViewer bound with a ControlTemplate (which works fine), a Grid inside (which overrides the default ScrollViewer and sets the ScrollBars "above" the content and finally I have some EventTriggers which set the Opacity of the ScrollBar s to 1 or 0 in a Storyboard. They also work well.

What I am missing is one of this triggers that sets the Opacity to 1 in case the scrolling takes place. It can be scrolled by mouse, mousewheel, stylus or touch, so it would be best to take the ScrollChanged of the ScrollViewer or the Scroll of the ScrollBars themselves. But I cannot make it work. Oh and btw. I cannot change the way it is build. So I have to make it by pure XAML inside the ControlTemplate (I know myself how to make it with code behind, but that is not possible here).

The style looks this way:

<Style TargetType="{x:Type controls:ViewerControl}">
    // ... some properties of no interest here
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type controls:ViewerControl}">
                <Border Grid.Row="0" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">
                    <ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" Name="PART_ScrollViewer">
                        <ScrollViewer.Template>
                            <ControlTemplate TargetType="{x:Type ScrollViewer}">
                                <Grid>
                                    <Grid.ColumnDefinitions>
                                        <ColumnDefinition Width="*" />
                                        <ColumnDefinition Width="Auto" />
                                    </Grid.ColumnDefinitions>
                                    <Grid.RowDefinitions>
                                        <RowDefinition Height="*"/>
                                        <RowDefinition Height="Auto"/>
                                    </Grid.RowDefinitions>
                                    <Grid.Triggers>
                                        <EventTrigger RoutedEvent="Grid.Loaded">
                                            <BeginStoryboard>
                                                <Storyboard>
                                                    <DoubleAnimation Duration="00:00:02" From="1" To="0" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="PART_VerticalScrollBar"/>
                                                    <DoubleAnimation Duration="00:00:02" From="1" To="0" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="PART_HorizontalScrollBar"/>
                                                </Storyboard>
                                            </BeginStoryboard>
                                        </EventTrigger>
                                        <EventTrigger RoutedEvent="Grid.MouseEnter">
                                            <BeginStoryboard>
                                                <Storyboard>
                                                    <DoubleAnimation Duration="0" From="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="PART_VerticalScrollBar"/>
                                                    <DoubleAnimation Duration="0" From="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="PART_HorizontalScrollBar"/>
                                                </Storyboard>
                                            </BeginStoryboard>
                                        </EventTrigger>
                                        <EventTrigger RoutedEvent="Grid.MouseLeave">
                                            <BeginStoryboard>
                                                <Storyboard>
                                                    <DoubleAnimation Duration="00:00:02" From="1" To="0" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="PART_VerticalScrollBar"/>
                                                    <DoubleAnimation Duration="00:00:02" From="1" To="0" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="PART_HorizontalScrollBar"/>
                                                </Storyboard>
                                            </BeginStoryboard>
                                        </EventTrigger>
                                    </Grid.Triggers>
                                    <ScrollContentPresenter Grid.ColumnSpan="2"
                                                            Grid.RowSpan="2"
                                                            ContentTemplate="{TemplateBinding ContentTemplate}"
                                                            Content="{TemplateBinding Content}"/>
                                    <ScrollBar Name="PART_VerticalScrollBar"
                                               HorizontalAlignment="Right"
                                               Opacity="0"
                                               Grid.Column="1"
                                               IsTabStop="False"
                                               Value="{TemplateBinding VerticalOffset}"
                                               Maximum="{TemplateBinding ScrollableHeight}"
                                               ViewportSize="{TemplateBinding ViewportHeight}"
                                               Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}"/>
                                    <ScrollBar Name="PART_HorizontalScrollBar"
                                               VerticalAlignment="Bottom"
                                               Orientation="Horizontal"
                                               Opacity="0"
                                               Grid.Row="1"
                                               IsTabStop="False"
                                               Value="{TemplateBinding HorizontalOffset}"
                                               Maximum="{TemplateBinding ScrollableWidth}"
                                               ViewportSize="{TemplateBinding ViewportWidth}"
                                               Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}"/>
                                </Grid>
                            </ControlTemplate>
                        </ScrollViewer.Template>
                        <StackPanel IsItemsHost="True" Name="PART_ItemsHost" VerticalAlignment="Center" HorizontalAlignment="Center"/>
                    </ScrollViewer>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

I tried it with something like this:

<ScrollViewer.Triggers>
    <EventTrigger RoutedEvent="ScrollViewer.ScrollChanged">
        <BeginStoryboard>
            <Storyboard>
                <DoubleAnimation Duration="0" From="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="PART_VerticalScrollBar"/>
                <DoubleAnimation Duration="0" From="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="PART_HorizontalScrollBar"/>
            </Storyboard>
        </BeginStoryboard>
    </EventTrigger>
</ScrollViewer.Triggers>

But if I put this directly inside the ScrollViewer it cannot find the ScrollBars and if I put it inside the Grid it is completly ignored (the Grid itself has no control over the ScrollViewer and seems not to bubble this up to it.

I found an answer where someone used the VisualStateManager, but this only seems to work in Silverlight, not in C#. It looked like this:

<VisualStateManager.VisualStateGroups>
    <VisualStateGroup x:Name="ScrollStates">
        <VisualStateGroup.Transitions>
            <VisualTransition GeneratedDuration="00:00:00.5"/>
        </VisualStateGroup.Transitions>
        <VisualState x:Name="Scrolling">
            <Storyboard>
                <DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="PART_VerticalScrollBar"/>
                <DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="PART_HorizontalScrollBar"/>
            </Storyboard>
        </VisualState>
        <VisualState x:Name="NotScrolling"/>
    </VisualStateGroup>
</VisualStateManager.VisualStateGroups>

Has someone an idea how to make this work?

CodePudding user response:

A coworker of mine found the answer:

<ControlTemplate.Triggers>
    <EventTrigger RoutedEvent="ScrollViewer.ScrollChanged">
        <BeginStoryboard>
            <Storyboard>
                <DoubleAnimation Duration="0" From="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="PART_VerticalScrollBar"/>
                <DoubleAnimation Duration="0" From="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="PART_HorizontalScrollBar"/>
                <DoubleAnimation BeginTime="00:00:02" Duration="00:00:01.5" From="1" To="0" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="PART_VerticalScrollBar"/>
                <DoubleAnimation BeginTime="00:00:02" Duration="00:00:01.5" From="1" To="0" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="PART_HorizontalScrollBar"/>
            </Storyboard>
        </BeginStoryboard>
    </EventTrigger>
</ControlTemplate.Triggers>

So if you put the EventTrigger inside the ControlTemplate.Triggers, it works like a charm... really cool. You only have to put these MouseEnter and MouseLeave inside the ScrollBars or else they are also triggered if the cursor enters the content of the ScrollViewer, not only on entering the ScrollBars.

  • Related