IsMouseOver 和 DragLeave 无法正常工作
IsMouseOver and DragLeave not working properly
我的 UI 上有一个 grid/panel 可以执行放下操作。该控件是一个网格,其中包含一些动态创建的内容(如果重要,则包含一个包含边框堆栈面板的视图框)。
当我将相关信息拖到网格中时,我希望它更改(重新生成)内部内容,当它掉落或离开时,它应该恢复正常。我的 'change it' 和 'put it back' 方法工作得很好,但我无法确定何时调用它们。
我在网格上有拖动进入和拖动离开事件。进入效果很好,但离开效果不好。一旦鼠标越过网格内内容的顶部,它就会触发拖动离开事件,因此它会更改内容(如果这将其放回子内容之外,它会再次触发拖动输入并闪烁) .
我的第一个想法是确定鼠标是否实际上仍在网格上,然后在不放回内容的情况下直接退出。但是,这似乎不起作用。这是我的拖放代码:
private void Grid_DragLeave(object sender, DragEventArgs e)
{
var wizard = DataContext as WizardVM;
var gd = sender as Grid;
if (wizard == null || gd == null || gd.IsMouseOver ||
gd.Children.Cast<FrameworkElement>().Any(x => x.IsMouseOver)) return;
wizard.Assembly.CollapsePreview();
}
如您所见,我什至尝试迭代网格的子项,看看鼠标悬停在其中任何一个上是否为 true 并放弃,但它仍然只是为所有这些返回 false 并崩溃。我认为 IsMouseOver 应该告诉我鼠标是否完全结束,即使它是控件的子项...
好的,经过更多研究后,我发现了一些似乎有效的方法。我最终不得不使用 VisualTreeHelper.HitTest 并使用具有回调的重载并获取所有命中项,即使它被遮盖了。这是我的最终代码:
void Grid_DragLeave(object sender, DragEventArgs e)
{
var wizard = DataContext as WizardVM;
var gd = sender as Grid;
if (wizard == null || gd == null) return;
Point pt = e.GetPosition(this);
hitResults.Clear();
VisualTreeHelper.HitTest(gd, null, GridHitTestResultCallback,
new PointHitTestParameters(pt));
if (!hitResults.Contains(gd))
{
wizard.Assembly.IsExpanded = false;
}
}
HitTestResultBehavior GridHitTestResultCallback(HitTestResult result)
{
hitResults.Add(result.VisualHit);
return HitTestResultBehavior.Continue;
}
基本上它获取与完整 window 相关的点,然后使用该点和我尝试使用的整体网格调用 HitTest。回调填充命中测试命中的视觉对象列表。最后,原始方法检查网格是否在命中列表中,如果不在,则将其折叠。
作为旁注,出于不相关的原因,我还从 .ExpandPreview() 和 .CollapsePreview() 方法更改为 .IsExpanded 属性 ...不想让任何人对这种变化感到困惑。 ..
我的 UI 上有一个 grid/panel 可以执行放下操作。该控件是一个网格,其中包含一些动态创建的内容(如果重要,则包含一个包含边框堆栈面板的视图框)。
当我将相关信息拖到网格中时,我希望它更改(重新生成)内部内容,当它掉落或离开时,它应该恢复正常。我的 'change it' 和 'put it back' 方法工作得很好,但我无法确定何时调用它们。
我在网格上有拖动进入和拖动离开事件。进入效果很好,但离开效果不好。一旦鼠标越过网格内内容的顶部,它就会触发拖动离开事件,因此它会更改内容(如果这将其放回子内容之外,它会再次触发拖动输入并闪烁) .
我的第一个想法是确定鼠标是否实际上仍在网格上,然后在不放回内容的情况下直接退出。但是,这似乎不起作用。这是我的拖放代码:
private void Grid_DragLeave(object sender, DragEventArgs e)
{
var wizard = DataContext as WizardVM;
var gd = sender as Grid;
if (wizard == null || gd == null || gd.IsMouseOver ||
gd.Children.Cast<FrameworkElement>().Any(x => x.IsMouseOver)) return;
wizard.Assembly.CollapsePreview();
}
如您所见,我什至尝试迭代网格的子项,看看鼠标悬停在其中任何一个上是否为 true 并放弃,但它仍然只是为所有这些返回 false 并崩溃。我认为 IsMouseOver 应该告诉我鼠标是否完全结束,即使它是控件的子项...
好的,经过更多研究后,我发现了一些似乎有效的方法。我最终不得不使用 VisualTreeHelper.HitTest 并使用具有回调的重载并获取所有命中项,即使它被遮盖了。这是我的最终代码:
void Grid_DragLeave(object sender, DragEventArgs e)
{
var wizard = DataContext as WizardVM;
var gd = sender as Grid;
if (wizard == null || gd == null) return;
Point pt = e.GetPosition(this);
hitResults.Clear();
VisualTreeHelper.HitTest(gd, null, GridHitTestResultCallback,
new PointHitTestParameters(pt));
if (!hitResults.Contains(gd))
{
wizard.Assembly.IsExpanded = false;
}
}
HitTestResultBehavior GridHitTestResultCallback(HitTestResult result)
{
hitResults.Add(result.VisualHit);
return HitTestResultBehavior.Continue;
}
基本上它获取与完整 window 相关的点,然后使用该点和我尝试使用的整体网格调用 HitTest。回调填充命中测试命中的视觉对象列表。最后,原始方法检查网格是否在命中列表中,如果不在,则将其折叠。
作为旁注,出于不相关的原因,我还从 .ExpandPreview() 和 .CollapsePreview() 方法更改为 .IsExpanded 属性 ...不想让任何人对这种变化感到困惑。 ..