WPF DragDrop事件元素跟隨

前一段時間項目裏面要實現一個鼠標拖動一個元素到另一個元素上面而且賦值的功能,因爲要在surface上運行,拖動的時候手指會擋住系統默認的拖動圖標,致使用戶意識不到本身是否是在拖動着東西,因此要解決這個問題。ide

初始想法

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

因爲原本就是沒有實現的方法,因此在此就不上代碼了。code

第二種方案

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

        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是一個元素效果,即開始拖動的時候,該元素會左右搖晃,讓用戶更清楚的知道,本身拖動的是哪一個元素。blog

        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);
        }

至此,功能完成。事件

相關文章
相關標籤/搜索