web-dev-qa-db-ja.com

scrollviewerの子要素がマウスホイールによるスクロールを妨げていますか?

わかりやすくするために簡略化した次のXAMLでマウスホイールのスクロールを機能させるのに問題があります。

<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>

視覚的には、結果はいくつかの既知のサイズの領域であり、コレクションから読み取られたDataFieldsは、任意の位置、サイズなどを持つTextBoxesで表すことができます。 ListBoxのスタイル付き「領域」が大きすぎて一度に表示できない場合は、水平および垂直スクロールが可能ですが、スクロールバーを使用した場合のみです。

人間工学と健全性を高めるために、マウスホイールでのスクロールが可能である必要があります。通常、ScrollViewerが自動的に処理しますが、ListBoxが親ScrollViewerそれらを見ることはありません。これまでのところ、ホイールスクロールが機能するのはIsHitTestVisible=FalseListBoxまたは親Gridのいずれかですが、その後は子要素のマウスイベントは機能しません。

ScrollViewerがマウスホイールイベントを確実に表示し、他の要素を子要素に維持するために何ができますか?

編集:ListBoxには組み込みのScrollViewerがあり、おそらく親ScrollViewerからホイールイベントを盗んでいること、およびコントロールテンプレートを指定すると無効にできることを知りました。それで問題が解決した場合は、この質問を更新します。

34
Tom

ControlTemplateを含まないListboxScrollViewerを指定すると、問題が解決します。詳細については、 この回答 およびこれら2つのMSDNページを参照してください。

ControlTemplate

リストボックスのスタイルとテンプレート

13
Tom

ビヘイビアーを作成し、それを親コントロール(スクロールイベントがバブリングする必要がある)にアタッチすることもできます。

// 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

これを実装する別の方法は、次のような独自のScrollViewerを作成することです。

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

私はそれが少し遅れていることを知っていますが、私のために働いた別の解決策があります。私は自分のスタックパネル/リストボックスをitemscontrol/gridに切り替えました。スクロールイベントが適切に機能する理由はわかりませんが、私の場合は機能します。

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

なりました

<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