WPF C# 预览放置/放置事件未触发(带有拖动装饰器)

7
我假设当检测到元素作为放置目标的拖动目标时,会触发previewdrop/drop事件。在这种情况下,我的放置目标是一个文本框,而我的拖动目标是一个标签。它们都是从数据库动态创建的。在实现DragAdorner之前,我的拖放功能很好用,但是使用DragAdorner后就无法正常工作了。我注意到在调试时我的previewdrop事件没有被触发。

以下是我的代码:

 tbox.Drop += new DragEventHandler(tbox_PreviewDrop); // text box , Drop Target
 tbox.DragOver += new DragEventHandler(tbox_DragOver);

Label lbl = new Label();  // Label , Drag Target 
             lbl.Content = s;
             lbl.Width = Double.NaN;
             lbl.Height = 40;
             lbl.FontSize = 19;
             lbl.PreviewMouseDown += new MouseButtonEventHandler(lbl_MouseDown);
             lbl.PreviewMouseMove += new MouseEventHandler(lbl_MouseMove);
            lbl.PreviewGiveFeedback += new GiveFeedbackEventHandler(lbl_GiveFeedback);


     private void lbl_MouseDown(object sender, MouseButtonEventArgs e)
    {
        startPoint = e.GetPosition(this);
      //  Mouse.OverrideCursor = Cursors.None;

    }

    private void lbl_MouseMove(object sender, MouseEventArgs e)
    {

        if (e.LeftButton == MouseButtonState.Pressed)
        {

          //  Mouse.OverrideCursor = Cursors.None;

            var source = sender as UIElement;
            Label lbl = sender as Label;
            Point current = e.GetPosition(this);
            Vector diff = startPoint - current;

            if (Math.Abs(diff.X) > SystemParameters.MinimumHorizontalDragDistance ||
                Math.Abs(diff.Y) > SystemParameters.MinimumVerticalDragDistance)
            {

                adorner = new DragAdorner(lbl, e.GetPosition(lbl));
                AdornerLayer.GetAdornerLayer(lbl).Add(adorner);

                var dragData = new DataObject(this);
                DragDrop.DoDragDrop(source, dragData, DragDropEffects.Copy);
                AdornerLayer.GetAdornerLayer(lbl).Remove(adorner);

            }
            startPoint = current;
        }
    }

    private void lbl_GiveFeedback(object sender, GiveFeedbackEventArgs e)
    {
        if (adorner != null)
        {
            Label lbl = sender as Label;
            var pos = lbl.PointFromScreen(GetMousePosition());
            adorner.UpdatePosition(pos);
            e.Handled = true;

        }
    }



private void tbox_PreviewDrop(object sender, DragEventArgs e)
        {

            (sender as TextBox).Text = string.Empty; // Empty the textbox from previous answer.
            (sender as TextBox).Background = Brushes.White;
            e.Effects = DragDropEffects.Move;
            e.Handled = true;

        }

        private void tbox_DragOver(object sender, DragEventArgs e)
        {
            e.Handled = true;
            e.Effects = DragDropEffects.Move;

        }
     [DllImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    internal static extern bool GetCursorPos(ref Win32Point pt);

    [StructLayout(LayoutKind.Sequential)]
    internal struct Win32Point
    {
        public Int32 X;
        public Int32 Y;
    };

    public static Point GetMousePosition()
    {
        Win32Point w32Mouse = new Win32Point();
        GetCursorPos(ref w32Mouse);
        return new Point(w32Mouse.X, w32Mouse.Y);
    }

    private Point startPoint;
    private DragAdorner adorner;

并且装饰器类文件:

 public class DragAdorner : Adorner {

public DragAdorner(UIElement adornedElement, Point offset)

    : base(adornedElement) {

    this.offset = offset;

    vbrush = new VisualBrush(AdornedElement);
    //vbrush.Opacity = .7;

}



public void UpdatePosition(Point location) {

    this.location = location;

    this.InvalidateVisual();

}



protected override void OnRender(DrawingContext dc) {

    var p = location;

    p.Offset(-offset.X, -offset.Y);

    dc.DrawRectangle(vbrush, null, new Rect(p, this.RenderSize));

}



private Brush vbrush;

private Point location;

private Point offset;

我看了这篇文章:http://www.adorkable.us/books/wpf_control_development.pdf(第103页),但对于我这个新手来说太复杂了。

是不是因为我的GiveFeedBack事件与其他事件冲突了呢?

2个回答

5

由于您的DragAdorner始终在光标下方,因此它将成为接收拖放操作的对象。如果您在Adorner的构造函数中设置IsHitTestVisible = false;,它应该可以解决这个问题。

即使您没有在Adorner上设置AllowDrop,但由于它位于光标下方,它将拦截拖放操作。但由于它不接受拖放操作,它只会取消它。

更新

另一个问题是,您在拖动操作中设置了允许的效果为DragDropEffects.Copy,但在DragOverDrop处理程序中,您尝试进行DragDropEffects.Move。这是行不通的,因为它们不是相同的操作。它们必须匹配。如果您想在拖动上启用两种操作,可以使用按位或指定两种操作:

DragDrop.DoDragDrop(source, dragData, DragDropEffects.Copy | DragDropEffects.Move);

更新 2

如果您想将一个string以外的任何东西拖放到TextBox中,您必须使用PreviewDropPreviewDragOver事件。否则,TextBox的默认处理将忽略所有其他内容。代码应如下所示:

tbox.PreviewDrop += new DragEventHandler(tbox_PreviewDrop); 
tbox.PreviewDragOver += new DragEventHandler(tbox_DragOver);

1
嗨,我已将IsHitTestVisible设置为false。但它仍然不会下降。抱歉,我不太理解你关于放置尝试和其他东西的第二个句子。 - user2376998
1
基本上,WPF 将在光标位置进行命中测试,并尝试在最顶层元素上放置。如果该元素不接受拖放操作,则该操作将被取消。这就是为什么禁用 Adorner 上的命中测试非常重要(Adorner 一直处于顶部)。 - Abe Heidebrecht
我已经禁用了hittest,但它不起作用,还有其他问题吗? - user2376998
不,你并没有拖动 Adorner。你正在拖动传递给 DragData 的任何内容。因此,在你的情况下,你可能正在传递一个 Window。这是 TextBox 默认不接受的。由于你说在添加 Adorner 之前它是工作的,我认为你没有遇到这个问题。我已经更新了答案,解释了如何解决这个问题。 - Abe Heidebrecht
1
好的,通过使用PreviewDragPreviewDrop,您可以操纵数据,以便将所需内容拖放到TextBox上。但是,如果您拖动一个字符串,TextBox可以自行处理它。 - Abe Heidebrecht
显示剩余3条评论

0
尝试为您的标签设置背景颜色,看看是否可以正常工作。

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