WPF如何通过子容器滚动父容器

5
我在尝试解决一个问题,即如何滚动位于滚动查看器内部的网格内容。当使用鼠标滚轮或触摸屏幕进行平移时,如果鼠标/触摸点位于空白区域上方,则网格可以正常滚动,但如果它位于某些控件上方(例如分组框),则无法滚动。是否有一些属性我错过了,以允许子面板滚动其父容器?
编辑: 我最初的布局描述有误。以下是我的情况的简化版本:
<Grid>
    <ScrollViewer Name="MainScrollViewer">
        <StackPanel>
            <ListBox />    <--Doesn't Scroll-->
            <Button />     <--Scrolls Fine-->
            <TextBlock />  <--Scrolls Fine-->
            <TextBox />    <--Scrolls Fine-->
            <DataGrid />   <--Doesn't Scroll-->
        </StackPanel>
    </ScrollViewer>
</Grid>

一位同事指出我的问题是由于像ListBoxes和DataGrids这样的控件本身包含ScrollViewers,这是有道理的。他的建议(虽然可行,但我们都认为比应该复杂)是在代码后台捕捉并重新抛出滚动事件(可能还要处理计算偏移量以滚动),以便它可以冒泡到“MainScrollViewer”中。
编辑2: 看起来实现这个唯一的方法是使用代码后台来处理父级中的PreviewMouseWheel事件。这可以解决问题,但我该如何为平移(在触摸屏上滚动)实现相同的功能呢?

你知道网格中包含一个ScrollViewer吗?没有必要将其放在ScrollViewer中...如果你安装了VS2013,你可以在C:\Program Files (x86)\Microsoft Visual Studio 12.0\Blend\SystemThemes\Wpf找到用于每个控件的模板。 - Tony Vitabile
当您说“网格”时,您是指“Grid”面板还是数据网格控件? - Mike Strobel
此外,如果控件的“背景”是“透明”的,则命中测试会失败。更改“背景”为其他颜色并进行测试。 - Tony Vitabile
1
@TonyVitabile 我认为你在考虑一个空的 Background。一个 Transparent 的背景应该可以很好地进行命中测试。唯一的例外是当你处于一个具有零 Alpha 值背景的分层窗口中时,WPF 将无法接收输入事件。 - Mike Strobel
@MikeStrobel 没错,那正是我所考虑的。 - Tony Vitabile
我编辑了我的问题并进行了更多的澄清。此外,我不能使用空背景方法,因为它在美学上不适合我的用户界面。 - AXG1010
3个回答

11

为您的滚动视图创建一个冒泡滚动行为:

using System.Windows;
using System.Windows.Input;
using System.Windows.Interactivity;

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)
    {
        if (!e.Handled)
        {
            e.Handled = true;
            var e2 = new MouseWheelEventArgs(e.MouseDevice, e.Timestamp, e.Delta) { RoutedEvent = UIElement.MouseWheelEvent };
            AssociatedObject.RaiseEvent(e2);
        }
    }
}
将此行为添加到您的滚动视图中:
 <ScrollViewer x:Name="Scroller">
                    <i:Interaction.Behaviors>
                        <ViewAddons:BubbleScrollEvent />
                    </i:Interaction.Behaviors>

3
这绝对是一种比标记答案更干净的方法。更具可重用性。 - Chandan
1
将此行为附加到外部ScrollViewer的效果与预期相同。在我看来,这应该是被接受的答案。 - Scott Solmer
对我来说,当我将它附加到子ListBox而不是父ListBox时,它开始工作了。我不知道为什么,也许我的情况不同。 - Jinjinov

6
使用 ScrollViewer 的 PreviewMouseWheel 事件和 ScrollToVerticalOffset 方法...
private void ScrollViewerOnPreviewMouseWheel(object sender, MouseWheelEventArgs e)
{
    var scv = sender as ScrollViewer;
    if (scv == null) return;
    scv.ScrollToVerticalOffset(scv.VerticalOffset - e.Delta);
    e.Handled = true;
}

1
是的,这或多或少是我和同事讨论过的内容。如果没有其他方法,那么我将使用这种方法,但似乎应该有一种更简单的方法,最好不涉及代码后台...在我的上面的示例中,StackPanel及其内容实际上是一个单独的用户控件,因此我必须将此代码插入到所有其调用父级中,似乎很可能以后会有人忘记这样做,并遇到我目前遇到的相同问题。 - AXG1010

0

首先,请确保您正在使用已经存在的技术。这可能会解决您的问题。

<Grid HorizontalAlignment="Left" Height="300" Margin="10,10,0,0" VerticalAlignment="Top" Width="497" ScrollViewer.VerticalScrollBarVisibility="Visible" />

虽然如果这样做不能解决问题,我知道这听起来很奇怪,但请将网格和问题对象的背景颜色设置为#00000000。(如果它尚未分配颜色/画刷)

<Grid HorizontalAlignment="Left" Height="300" Margin="10,10,0,0" VerticalAlignment="Top" Width="497" Background="#00000000"/>

我知道这很奇怪,但每当我遇到这些问题时,它总是有效的。我不知道为什么它有效。可能与透明度有关。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接