5

如果 WPF 列表框在按住鼠标按钮时收到 MouseMove 事件,它将更改列表框的选择。也就是说,如果您在项目#1 上单击鼠标,然后拖动到项目#2,它将取消选择项目#1 并选择项目#2。我怎样才能防止这种情况?

那是简短的版本。稍长的版本是这样的:当用户双击我的 ListBox 中的一个项目时,我对我的布局进行了其他更改,其中包括在 ListBox 上方显示其他控件。这会将 ListBox 向下移动,这意味着鼠标现在位于与用户双击时不同的 ListBoxItem 上。

由于我做出这些布局更改是为了响应 DoubleClick 事件(这是一个鼠标按下事件),因此很可能在此布局更改完成时仍会按下鼠标按钮,这意味着 WPF 将向 ListBox 发送一个 MouseMove 事件(因为鼠标相对于 ListBox 的位置已经改变)。ListBox 将此视为拖动,并选择现在位于鼠标下的事件。

我不希望选择在我获得双击事件和用户释放鼠标的时间之间发生变化(这可能是在布局更改之后)。我怀疑实现这一点的最简单方法是禁用“拖动时更改选择”行为,但我愿意接受其他建议。

如何在双击时“锁定”选择,并防止它在 mouseup 之前更改?

4

1 回答 1

20

ILSpy中进行了一些挖掘之后,我发现没有禁用“拖动选择”行为的属性,也没有可以标记为已处理的事件来停止它。

但是有一个很好的转折点来改变这种行为:ListBoxItem.OnMouseEnter 是虚拟的,它回调到列表框来改变选择。它似乎没有做任何其他实质性的事情,所以我需要做的就是覆盖它并且什么都不做。

编辑:事实证明,当您在列表框内移动鼠标时,上面的内容只会使选择保持不变。如果您将鼠标移动到列表框的上方或下方,这将无济于事——然后自动滚动启动并移动选择。大多数自动滚动代码又是在非虚拟方法中;看起来防止自动滚动的最佳方法可能是禁用鼠标捕获。ListBoxItem 上的另一个覆盖可以解决这个问题。

看起来使用我自己的 ListBoxItem 后代的最佳方法是从 ListBox 下降。最终代码如下所示:

public class ListBoxEx : ListBox
{
    protected override DependencyObject GetContainerForItemOverride()
    {
        return new ListBoxExItem();
    }
    protected override bool IsItemItsOwnContainerOverride(object item)
    {
        return item is ListBoxExItem;
    }
}
public class ListBoxExItem : ListBoxItem
{
    private Selector ParentSelector
    {
        get { return ItemsControl.ItemsControlFromItemContainer(this) as Selector; }
    }

    protected override void OnMouseEnter(MouseEventArgs e)
    {
    }
    protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
    {
        base.OnMouseLeftButtonDown(e);
        ParentSelector?.ReleaseMouseCapture();
    }
}
于 2011-04-14T21:40:49.330 回答