当鼠标进入WPF ComboBox下拉列表时防止滚动

28
当ComboBox有大量项目时,其下拉列表将变得可滚动。当用户调用此下拉列表并将鼠标光标从底部进入下拉列表的边界时,下拉列表会立即向下滚动一个或多个项目 (来自goobering:当通过底边界退出边界时也会发生)。
由于列表在从顶部进入边界时不会向上滚动,因此这种滚动方式不太直观。
如何禁用自动滚动行为?
在Visual Studio中,可以通过代码编辑器导航栏上的成员下拉列表(CTRL+F2)观察到此行为。

2
为了澄清这个行为,似乎每当光标从ComboBox下拉列表的最低边界向上或向下移动时都会触发。 - goobering
你正在运行哪个版本的.NET? - David
1
我已经在Microsoft Connect上提交了反馈,以防这是一个bug。 - JoeGaggler
4个回答

18

解决这个问题的一种方法是使用行为(或类似行为的附加属性)来订阅ComboBoxItemsRequestBringIntoView事件,然后将RequestBringIntoViewEventArgs.Handled设置为true。 也可以使用EventSetter和代码后台在小范围内完成此操作。

 <Style TargetType="ComboBoxItem">                    
     <EventSetter Event="RequestBringIntoView" Handler="OnRequestBringIntoView"/>
 </Style>

private void OnRequestBringIntoView(object sender, RequestBringIntoViewEventArgs e)
{
    //Allows the keyboard to bring the items into view as expected:
    if (Keyboard.IsKeyDown(Key.Down) || Keyboard.IsKeyDown(Key.Up))
        return;            

    e.Handled = true;            
}

编辑

我发现您可以通过在 ItemsPanel 上处理 RequestBringIntoView 事件来获得相同的效果,而不是处理项本身。但是结果相同:

<ComboBox.ItemsPanel>
    <ItemsPanelTemplate>
        <StackPanel RequestBringIntoView="OnRequestBringIntoView"/>
    </ItemsPanelTemplate>
</ComboBox.ItemsPanel>

1
我明白了。这段代码确实避免了鼠标事件的滚动。如果用户希望通过键盘事件进行滚动,需要更多的逻辑来实现,但是这段代码回答了我的最初问题。 - JoeGaggler
1
@JoeGaggler 我发现它也可以从ItemsPanel事件中工作,这样可以减少挂钩的事件处理程序数量。 - Andrew Hanlon
2
@JoeGaggler 我添加了一个简单的示例检查,以查看键盘的上/下键是否被按下。有趣的是,页面向上/向下可以在没有检查的情况下工作。 - Andrew Hanlon
1
第一个示例会影响ComboBoxItems的样式,而第二个示例则不会,因此我更愿意使用第二个示例。 - Szabolcs Antal
1
我找到的最佳解决方案是在 ComboBox_RequestBringIntoView 方法中添加 if (((UIElement)e.Source).IsKeyboardFocusWithin) e.Handled = true;。这可以防止不必要的自动滚动,但也可以让键盘输入过滤正常工作。 - Szabolcs Antal
显示剩余2条评论

5
据我所知,这似乎是由于光标停留在下方的项目被“部分显示”,即容器截短了该项。当鼠标经过这样的部分项目时,WPF会滚动整个项目以便查看,这有时会导致另一个部分项位于底部。
在Winforms中,可以通过设置“.IntegralHeight”来解决此问题,但据我所知,在WPF中不存在这样的属性。如果组合框中的所有项具有相同的高度,则可以将组合框列表的高度绑定到多个项高度的倍数上,例如,显示10个20像素高的项,则将其设置为200。

这是一个很好的理论,但当我尝试时似乎并没有起作用。我能够使用ItemTemplate属性显式设置项目高度,然后将MaxDropDownHeight设置为项目高度的整数倍。当将鼠标移动到下拉框边框上时,仍会出现此行为。我还尝试调整高度+/- 1px以确保没有偏差问题。 - JoeGaggler

1

Andrew Hanlon提供的选定答案可以防止列表在打开时滚动到所选项目。

我不得不将以下内容添加到事件处理程序中("list"是ComboBox):

private void OnRequestBringIntoView(object sender, RequestBringIntoViewEventArgs e)
{
    //Allows the keyboard to bring the items into view as expected:
    if (Keyboard.IsKeyDown(Key.Down) || Keyboard.IsKeyDown(Key.Up))
        return;

    // Allows to bring the selected item into view:
    if (((ComboBoxItem)e.TargetObject).Content == list.SelectedItem)
        return;

    e.Handled = true;
}

0

我在我的应用程序中遇到了同样的问题,所以我通过对下拉框的PART_Popup进行样式设置来解决这个问题:

<Popup x:Name="PART_Popup"
       IsOpen="{TemplateBinding IsDropDownOpen}"
       AllowsTransparency="True" 
       Focusable="False"
       PopupAnimation="Slide">

    <Grid MinWidth="{TemplateBinding ActualWidth}"
          MaxHeight="{TemplateBinding MaxDropDownHeight}">

        <Border x:Name="dropDownBorder"
                Background="White"
                BorderThickness="1"
                BorderBrush="Gray"
                Margin="0 2 0 0"/>

        <ScrollViewer SnapsToDevicePixels="True">
            <VirtualizingStackPanel IsItemsHost="True" 
                                    KeyboardNavigation.DirectionalNavigation="Contained" />
        </ScrollViewer>

    </Grid>
</Popup>

如果在ScrollViewer中添加Margin(例如:<ScrollViewer Margin="1" ...),它会自动开始向下滚动。

这对我没有任何作用。 - Edgar

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