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 DataField
s lus à partir d'une collection peuvent être représentés avec TextBox
es 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.
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:
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>
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);
}
}
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>