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

Prevent scrolling when mouse enters WPF ComboBox dropdown

ComboBox 包含大量项目时,其下拉列表将变为可滚动。当用户调用此下拉菜单并将鼠标光标从底部移动到下拉菜单的边界时,下拉菜单会立即向下滚动列表中的一个或多个项目(from goobering:退出边界时也会发生底边).

这种滚动不直观,因为从顶部进入边界时列表不会向上滚动。

我们如何禁用自动滚动行为?

在 Visual Studio 中,可以通过代码编辑器导航栏上的成员下拉列表观察此行为 (CTRL+F2 ).

据我所知,这似乎是由灯底部的项目 "partially displayed" 引起的,其中项目被容器截断了。当鼠标经过像这样的部分项目时,WPF 会将整个项目滚动到视图中,有时会在底部留下另一个部分项目。

在 Winforms 中,这可以通过设置 .IntegralHeight 来解决,但据我所知,WPF 中不存在这样的 属性。如果您的组合框中的所有项目都具有相同的高度,您可以将组合框列表的高度绑定到项目高度的倍数,例如,显示 10 x 20 像素高的项目,将其设置为 200。

解决这个问题的一种方法是使用行为(或者更确切地说,行为类似附加 属性)来订阅 ComboBoxItemsRequestBringIntoView 事件,然后设置 RequestBringIntoViewEventArgs.Handled 为真。 这也可以使用 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>

来自 Andrew Hanlon 的所选答案阻止列表在打开时滚动到所选项目。

我必须将其添加到事件处理程序中(“列表”是 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;
}

我在我的应用程序中遇到了同样的问题,所以我设计了 Combobox 的 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" ...),它会开始自动向下滚动。