Éléments enfants de scrollviewer empêchant le défilement avec la roue de la souris?
j'ai un problème pour faire fonctionner le défilement de la souris 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 un espace de taille connue où DataField
S lu à partir d'une collection peut être représenté avec TextBox
qui ont une position, une taille, etc. arbitraire. Dans les cas où l' est trop grande pour être affichée en même temps, le défilement horizontal et vertical est possible, mais seulement avec le défilement bars.
pour une meilleure ergonomie et santé, le défilement de la souris devrait être possible, et normalement ScrollViewer
manipuler automatiquement, mais l' ListBox
semble transmettre ces événements de telle sorte que le parent ScrollViewer
ne les voit jamais. Pour l'instant j'ai seulement été en mesure d'obtenir de la roue de défilement de travail à définir IsHitTestVisible=False
pour le ListBox
ou le parent Grid
, mais bien sûr aucun des évènements souris de l'élément enfant ne fonctionne après cela.
Que puis-je faire pour assurer ScrollViewer
voit les évènements de la roue de la souris tout en préservant les autres pour les éléments enfants?
Edit: je viens d'apprendre que ListBox
intégré ScrollViewer
qui est probablement en train de voler les évènements de la roue au parent ScrollViewer
et que la spécification d'un modèle de contrôle peut le désactiver. Je vais mettre à jour cette question si ça résout le problème.
4 réponses
Vous pouvez aussi créer un behavior et l'attacher au contrôle parent (dans lequel les événements de scroll devraient être présents).
// 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>
Spécification ControlTemplate
pour les Listbox
ce qui n'inclut pas un ScrollViewer
résout le problème. Voir cette réponse et ces deux pages MSDN pour plus d'informations:
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/grille. Je ne sais pas pourquoi les événements de scroll 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>
devient
<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>
une autre façon d'implémenter ceci, 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);
}
}