当WPF布局改变时,防止ListBox在鼠标下选择项目

5
如果WPF ListBox在鼠标按钮按下时接收到MouseMove事件,它将更改ListBox的选择。也就是说,如果您在项目#1上单击鼠标,然后拖动到项目#2上,它将取消选择项目#1并选择项目#2。我该如何防止这种情况发生? 简短的版本是这样的:当用户双击我的ListBox中的项目时,我会对我的布局进行其他更改,包括在ListBox上方显示其他控件。这将使ListBox向下移动,这意味着鼠标现在位于与用户双击时不同的ListBoxItem上。
由于我响应DoubleClick事件(这是一个MouseDown事件)进行这些布局更改,因此当此布局更改完成时,鼠标按钮很可能仍被按下,这意味着WPF会发送MouseMove事件以ListBox为参考系(因为鼠标相对于ListBox的位置已经发生了变化)。ListBox将其视为拖动,并选择现在在鼠标下方的项目。
我不希望在我获得double-click事件和用户释放鼠标之间(这可能远远超出布局更改时间)选择发生变化。我怀疑实现这一点的最简单方法是禁用“拖动更改选择”行为,但我接受其他建议。
我如何在双击时“锁定”选择,并防止它在mouseup之前更改?
1个回答

20

经过在ILSpy的探索后,我发现没有禁用“拖动选择”行为的属性,也没有可以标记为Handled来停止它的事件。

但有一个很好的转折点可以改变这种行为:ListBoxItem.OnMouseEnter是虚拟的,并且它回调到listbox以更改选择。除此之外,它似乎没有做任何实质性的事情,所以我只需要重写它并什么也不做。

编辑:事实证明,上述方法仅在您在列表框内移动鼠标时防止了选择的更改。如果将鼠标移动到列表框上方或下方,则自动滚动会启动并移动选择。大部分自动滚动代码再次位于非虚拟方法中;最好的防止自动滚动的方法可能是禁用鼠标捕获。对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();
    }
}

帮助解决了列表项拖放操作期间的选择更改问题。谢谢。 - Bill Campbell
如果我们在鼠标按下事件中移除所选项目,则ParentSelector将为null。我们可以使用空值检查ParentSelector?.ReleaseMouseCapture()来解决这个问题。 - J3soon
@J3soon 好建议。已更新。 - Joe White

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