web-dev-qa-db-fra.com

Éléments enfants de scrollviewer empêchant le défilement avec la molette de la souris?

J'ai un problème pour que le défilement de la molette de la souris fonctionne dans le XAML suivant, que j'ai simplifié pour plus de clarté:

<ScrollViewer
HorizontalScrollBarVisibility="Visible"
VerticalScrollBarVisibility="Visible"
CanContentScroll="False"
>
    <Grid
    MouseDown="Editor_MouseDown"
    MouseUp="Editor_MouseUp"
    MouseMove="Editor_MouseMove"
    Focusable="False"
    >
        <Grid.Resources>
            <DataTemplate
            DataType="{x:Type local:DataFieldModel}"
            >
                <Grid
                Margin="0,2,2,2"
                >
                    <TextBox
                    Cursor="IBeam"
                    MouseDown="TextBox_MouseDown"
                    MouseUp="TextBox_MouseUp"
                    MouseMove="TextBox_MouseMove"
                    />
                </Grid>
            </DataTemplate>
        </Grid.Resources>
        <ListBox
        x:Name="DataFieldListBox"
        ItemsSource="{Binding GetDataFields}"
        SelectionMode="Extended"
        Background="Transparent"
        Focusable="False"
        >
            <ListBox.ItemsPanel>
                <ItemsPanelTemplate>
                    <Canvas />
                </ItemsPanelTemplate>
            </ListBox.ItemsPanel>
            <ListBox.ItemContainerStyle>
                <Style
                TargetType="ListBoxItem"
                >
                    <Setter
                    Property="Canvas.Left"
                    Value="{Binding dfX}"
                    />
                    <Setter
                    Property="Canvas.Top"
                    Value="{Binding dfY}"
                    />
                </Style>
            </ListBox.ItemContainerStyle>
        </ListBox>
    </Grid>
</ScrollViewer>

Visuellement, le résultat est une zone d'une certaine taille connue où les DataFields lus à partir d'une collection peuvent être représentés avec TextBoxes qui ont une position, une taille et ainsi de suite arbitraires. Dans les cas où la "zone" de style de ListBox est trop grande pour être affichée d'un seul coup, le défilement horizontal et vertical est possible, mais uniquement avec les barres de défilement.

Pour une meilleure ergonomie et raison, le défilement de la molette de la souris devrait être possible et normalement ScrollViewer le gérerait automatiquement, mais le ListBox semble gérer ces événements de telle sorte que le parent ScrollViewer ne les voit jamais. Jusqu'à présent, je n'ai pu faire fonctionner le défilement des roues que par réglage de IsHitTestVisible=False pour le ListBox ou le parent Grid, mais bien sûr aucun des événements de la souris de l'élément enfant ne fonctionne après cela.

Que puis-je faire pour m'assurer que ScrollViewer voit les événements de la molette de la souris tout en préservant les autres pour les éléments enfants?

Edit: je viens d'apprendre que ListBox a un ScrollViewer intégré qui vole probablement les événements de roue du parent ScrollViewer et que la spécification d'un modèle de contrôle peut le désactiver. Je mettrai à jour cette question si cela résout le problème.

34
Tom

La spécification d'un ControlTemplate pour le Listbox qui n'inclut pas un ScrollViewer résout le problème. Voir cette réponse et ces deux pages MSDN pour plus d'informations:

ControlTemplate

Styles et modèles ListBox

13
Tom

Vous pouvez également créer un comportement et l'attacher au contrôle parent (dans lequel les événements de défilement doivent se propager).

// Used on sub-controls of an expander to bubble the mouse wheel scroll event up 
public sealed class BubbleScrollEvent : Behavior<UIElement>
{
    protected override void OnAttached()
    {
        base.OnAttached();
        AssociatedObject.PreviewMouseWheel += AssociatedObject_PreviewMouseWheel;
    }

    protected override void OnDetaching()
    {
        AssociatedObject.PreviewMouseWheel -= AssociatedObject_PreviewMouseWheel;
        base.OnDetaching();
    }

    void AssociatedObject_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
    {
        e.Handled = true;
        var e2 = new MouseWheelEventArgs(e.MouseDevice, e.Timestamp, e.Delta);
        e2.RoutedEvent = UIElement.MouseWheelEvent;
        AssociatedObject.RaiseEvent(e2);
    }
}

<SomePanel>
            <i:Interaction.Behaviors>
                <viewsCommon:BubbleScrollEvent />
            </i:Interaction.Behaviors>
</SomePanel>
56
JoeB

Une autre façon de l'implémenter est de créer votre propre ScrollViewer comme ceci:

public class MyScrollViewer : ScrollViewer
{
    protected override void OnMouseWheel(MouseWheelEventArgs e)
    {
        var parentElement = Parent as UIElement;
        if (parentElement != null)
        {
            if ((e.Delta > 0 && VerticalOffset == 0) ||
                (e.Delta < 0 && VerticalOffset == ScrollableHeight))
            {
                e.Handled = true;

                var routedArgs = new MouseWheelEventArgs(e.MouseDevice, e.Timestamp, e.Delta);
                routedArgs.RoutedEvent = UIElement.MouseWheelEvent;
                parentElement.RaiseEvent(routedArgs);
            }
        }

        base.OnMouseWheel(e);
    }
}
3
kahr

Je sais qu'il est un peu tard mais j'ai une autre solution qui a fonctionné pour moi. J'ai changé mon stackpanel/listbox pour un itemscontrol/grid. Je ne sais pas pourquoi les événements de défilement fonctionnent correctement, mais ils le font dans mon cas.

<ScrollViewer VerticalScrollBarVisibility="Auto" PreviewMouseWheel="ScrollViewer_PreviewMouseWheel">
                <StackPanel Orientation="Vertical">
                    <ListBox ItemsSource="{Binding DrillingConfigs}" Margin="0,5,0,0">
                        <ListBox.ItemTemplate>
                            <DataTemplate>

est devenu

<ScrollViewer VerticalScrollBarVisibility="Auto" PreviewMouseWheel="ScrollViewer_PreviewMouseWheel">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <ItemsControl ItemsSource="{Binding DrillingConfigs}" Margin="0,5,0,0" Grid.Row="0">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
2
sean.net