简体   繁体   English

当鼠标进入 WPF ComboBox 下拉菜单时防止滚动

[英]Prevent scrolling when mouse enters WPF ComboBox dropdown

When a ComboBox has a large number of items, its dropdown will become scrollable.ComboBox有大量项目时,其下拉列表将变为可滚动的。 When the user invokes this dropdown, and moves the mouse cursor to enter the bounds of the dropdown from the bottom, the dropdown immediately scrolls one or more items down the list ( from goobering: also happens when exiting the bounds via the bottom edge ).当用户调用这个下拉菜单,并移动鼠标光标从底部进入下拉菜单的边界时,下拉菜单会立即滚动列表中的一个或多个项目(来自 goobering:当通过底部边缘退出边界时也会发生)。

This scrolling is not intuitive, as the list does not scroll upwards when entering the bounds from the top.这种滚动并不直观,因为从顶部进入边界时列表不会向上滚动。

How can we disable the automatic scrolling behavior?我们如何禁用自动滚动行为?

In Visual Studio this behavior can be observed via the member dropdown on the code editor's navigation bar ( CTRL + F2 ).在 Visual Studio 中,可以通过代码编辑器导航栏 ( CTRL + F2 ) 上的成员下拉列表观察此行为。

One way to solve this is to use a Behaviour (or rather behaviour-like Attached Property) to subscribe to the RequestBringIntoView event of the ComboBoxItems and then set the RequestBringIntoViewEventArgs.Handled to true.解决此问题的一种方法是使用 Behavior(或类似行为的附加属性)订阅ComboBoxItemsRequestBringIntoView事件,然后将RequestBringIntoViewEventArgs.Handled设置为 true。 This can also be done on a small scale using an EventSetter and codebehind.这也可以使用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;            
}

Edit编辑

I found that you can get the same effect by handling the RequestBringIntoView event on the ItemsPanel rather than the items themselves.我发现,您可以通过在操控性上的RequestBringIntoView事件而得到相同的效果ItemsPanel ,而不是项目本身。 But same result:但同样的结果:

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

From what I can tell this seems to be caused by the items at the bottom of the light being "partially displayed", where an item is truncated by the container.据我所知,这似乎是由灯底部的项目“部分显示”引起的,其中项目被容器截断。 When the mouse goes over a partial item like this WPF scrolls the entire item in to view, which can sometimes leave another partial item at the bottom.当鼠标移到像这样的部分项目上时,WPF 会滚动整个项目以进行查看,这有时会在底部留下另一个部分项目。

In Winforms this can be fixed by setting the .IntegralHeight , but no such property exists in WPF from what I can tell.在 Winforms 中,这可以通过设置.IntegralHeight ,但据我所知,WPF 中不存在这样的属性。 If all the items in your combobox have the same height you could bind the height of the list of the combobox to a multiple of the item height, for example, display 10 x 20px tall items, set it to 200.如果组合框中的所有项目具有相同的高度,您可以将组合框列表的高度绑定到项目高度的倍数,例如,显示 10 x 20px 高的项目,将其设置为 200。

The selected answer from Andrew Hanlon avoids the list to scroll to the selected item when opened. Andrew Hanlon 的选定答案避免了列表在打开时滚动到所选项目。 I also had to add this into the event handler ("list" is the ComboBox):我还必须将其添加到事件处理程序中(“列表”是 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;
}

I'd same issue in my application so I've styled the Combobox's PART_Popup to solve the issue in this way:我在我的应用程序中遇到了同样的问题,所以我已经设计了 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>

If you add Margin in ScrollViewer (eg. <ScrollViewer Margin="1" ... ) it'd start scrolling down automatically.如果您在ScrollViewer添加Margin (例如<ScrollViewer Margin="1" ... ),它会自动开始向下滚动。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM