WPF DragDrop事件元素跟隨


前一段時間項目里面要實現一個鼠標拖動一個元素到另外一個元素上面並且賦值的功能,由於要在surface上運行,拖動的時候手指會擋住系統默認的拖動圖標,導致用戶意識不到自己是不是在拖動着東西,所以要解決這個問題。

初始想法

一開始在的設想是,拖動開始時新建一個元素要拖動的元素,然后設置次元素跟隨鼠標移動,這里遇到個問題就是,當使用DoDragDrop事件的時候,捕捉不到鼠標的坐標變動,以至於無法讓新建的元素跟隨移動。要實現功能必須放棄DoDragDrop事件,但是當時很多已經寫好的功能都是圍繞這個事件的,不想再改,於是開始探索新的方式,雖然一開始浪費了一點時間,但是好處也不是沒有,比如發現了GiveFeedback事件,於是就想到了第二種方案。

由於本來就是沒有實現的方法,所以在此就不上代碼了。

第二種方案

ButtonDown觸發的時候,給元素添加MouseMove事件,當MouseMove觸發的時候,獲取到當前元素並生成圖像轉換為Cursor格式,在GiveFeedback中用以改變鼠標樣式,添加GiveFeedback事件並啟動DoDrapDrop。

        public void ViewElemenMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            var viewElement = sender as ViewElement;
            if (viewElemen == null) return;
            viewElemen.MouseMove += ViewElemenOnPreviewMouseMove;
        }

        private void PatientTitleOnPreviewMouseMove(object sender, MouseEventArgs mouseEventArgs)
        {
            var viewElement = sender as ViewElement;
            if (viewElement == null) return;
            HoloCursor = FormUtility.CreateCursor(viewElement);
            viewElemen.RockStart();
            viewElemen.GiveFeedback += DragSource_GiveFeedback;
            DragDrop.DoDragDrop(viewElemen, Model, DragDropEffects.Copy);           
            viewElemen.MouseMove -= ViewElemenOnPreviewMouseMove;
            viewElemen.GiveFeedback -= DragSource_GiveFeedback;
            Mouse.SetCursor(Cursors.Arrow);
        }

        void DragSource_GiveFeedback(object sender, GiveFeedbackEventArgs e)
        {
            var viewElement = sender as ViewElement;
            if (viewElemen == null) return;
            Mouse.SetCursor(HoloCursor);
            e.UseDefaultCursors = false;
            e.Handled = true;
        }
之所以把這么麻煩是因為,鼠標點擊事件還要有其他的業務操作,所以只能加在mousemove里面。
根據元素生成鼠標 FormUtility.CreateCursor:

        public static Cursor CreateCursor(UIElement element)
        {
            element.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
            element.Arrange(new Rect(new Point(), element.DesiredSize));

            var rtb =
              new RenderTargetBitmap(
                (int)element.DesiredSize.Width,
                (int)element.DesiredSize.Height,
                96, 96, PixelFormats.Pbgra32);

            rtb.Render(element);

            var encoder = new PngBitmapEncoder();
            encoder.Frames.Add(BitmapFrame.Create(rtb));

            using (var ms = new MemoryStream())
            {
                encoder.Save(ms);
                using (var bmp = new System.Drawing.Bitmap(ms))
                {
                    return InternalCreateCursor(bmp);
                }
            }
        }
        private static Cursor InternalCreateCursor(System.Drawing.Bitmap bmp)
        {
            var iconInfo = new NativeMethods.IconInfo();
            NativeMethods.GetIconInfo(bmp.GetHicon(), ref iconInfo);

            iconInfo.xHotspot = 125;
            iconInfo.yHotspot = 65;
            iconInfo.fIcon = false;

            SafeIconHandle cursorHandle = NativeMethods.CreateIconIndirect(ref iconInfo);
            return CursorInteropHelper.Create(cursorHandle);
        }
       [SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)]
        private class SafeIconHandle : SafeHandleZeroOrMinusOneIsInvalid
        {
            public SafeIconHandle()
                : base(true)
            {
            }

            override protected bool ReleaseHandle()
            {
                return NativeMethods.DestroyIcon(handle);
            }
        }
        private static class NativeMethods
        {
            public struct IconInfo
            {
                public bool fIcon;
                public int xHotspot;
                public int yHotspot;
                public IntPtr hbmMask;
                public IntPtr hbmColor;
            }

            [DllImport("user32.dll")]
            public static extern SafeIconHandle CreateIconIndirect(ref IconInfo icon);

            [DllImport("user32.dll")]
            public static extern bool DestroyIcon(IntPtr hIcon);

            [DllImport("user32.dll")]
            [return: MarshalAs(UnmanagedType.Bool)]
            public static extern bool GetIconInfo(IntPtr hIcon, ref IconInfo pIconInfo);
        }

RockStart是一個元素效果,即開始拖動的時候,該元素會左右搖晃,讓用戶更清楚的知道,自己拖動的是哪個元素。

        private Storyboard _sb = new Storyboard(){ FillBehavior = FillBehavior.Stop };

        public ViewElement()
        {
            InitializeComponent();
            _sb.AutoReverse = true;
            var dbAscending1 = new DoubleAnimation(0, 3, new Duration(TimeSpan.FromMilliseconds(100)));
            _sb.Children.Add(dbAscending1);
            Storyboard.SetTarget(dbAscending1, Border);
            Storyboard.SetTargetProperty(dbAscending1, new PropertyPath("(Rectangle.RenderTransform).(RotateTransform.Angle)"));
            var dbAscending2 = new DoubleAnimation(3, -3, new Duration(TimeSpan.FromMilliseconds(200)));
            _sb.Children.Add(dbAscending2);
            Storyboard.SetTarget(dbAscending2, Border);
            Storyboard.SetTargetProperty(dbAscending2, new PropertyPath("(Rectangle.RenderTransform).(RotateTransform.Angle)"));
        }

        public void RockStart()
        {
             Dispatcher.InvokeAsync(() => _sb.Begin(), DispatcherPriority.Background);
        }

至此,功能完成。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM