silverlight:对象拖动的优雅解决方案

对象拖动是一个老生常谈的话题,在SL上要实现对象拖动,一般有三种思路:

一、基于Canvas绝对定位布局的拖动
这种处理方法最简单,修改对象的Canvas.Top与Canvas.Left即可,简单明了!
在线案例: silverlight图片局部放大效果
但是很多时候,我们采用的布局并不是Canvas,如果仅仅为了实现对象拖动,把整个布局重构,代价太大,有点得不偿失。

二、基于对象Margin值的拖动
Margin是对象的通用属性,通过改变Margin值理论上可在任何布局下,重新定位对象的位置。
在线案例: silverlight:类似iBaidu,iGoogle的拖放功能
缺点就是算法处理有些小复杂,初次看着有点晕。
三、基于TranslateTransform偏移量的拖动
每个对象都可以设置一系列RenderTransform,以实现变形、旋转、偏移等多种很Cool的效果。这也是一种通用的做法,不局限于某种特定的布局方法。
而且可以借助Behaviour将其封装起来,直接应用于多个对象,这也是我个人认为最 优雅的解决方案。
封装代码如下:
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Interactivity;
using System.Windows.Media;
using System.Windows.Media.Imaging;

namespace SLControls
{
    public class Drag : Behavior
    {
        public static readonly DependencyProperty IsMovableProperty =
            DependencyProperty.Register("IsMovable", typeof(bool),
                                        typeof(Drag), new PropertyMetadata(null));

        [Category("Target Properties")]
        public bool IsMovable { get; set; }

        private bool _isDragging = false;
        private Point _offset;
        private readonly TranslateTransform _elementTranslate = new TranslateTransform();
        private TranslateTransform _imgTranslate = new TranslateTransform();
        private Image _img = new Image();

        /// 
        /// Drag行为附加到对象上时触发
        /// 
        protected override void OnAttached()
        {
            base.OnAttached();

            AssociatedObject.Loaded += AssociatedObjectLoaded;
            
            //先将对象置于左上角
            AssociatedObject.HorizontalAlignment = HorizontalAlignment.Left;
            AssociatedObject.VerticalAlignment = VerticalAlignment.Top;

            
            AssociatedObject.MouseLeftButtonDown += AssociatedObjectMouseLeftButtonDown;
        }

        void AssociatedObjectLoaded(object sender, RoutedEventArgs e)
        {
            //默认先给对象创建一个TranslateTransform
            AssociatedObject.RenderTransform = _elementTranslate;
        }

        /// 
        /// Drag行为从对象剥离时触发
        /// 
        protected override void OnDetaching()
        {
            base.OnDetaching();
            //移除鼠标左键事件处理
            AssociatedObject.MouseLeftButtonDown -= AssociatedObjectMouseLeftButtonDown;
        }

        /// 
        /// 动象拖动时的处理
        /// 
        /// 
        /// 
        private void AssociatedObjectMouseMove(object sender, System.Windows.Input.MouseEventArgs e)
        {
            if (!_isDragging) return;
            FrameworkElement parent = _img.Parent as FrameworkElement;
            Point newPosition = e.GetPosition(parent);

            //移动的其实只是对象的"影子副本"
            _imgTranslate.X = (newPosition.X - _offset.X);
            _imgTranslate.Y = (newPosition.Y - _offset.Y);
        }

        /// 
        /// 托运结束时的处理
        /// 
        /// 
        /// 
        private void AssociatedObjectMouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            if (!_isDragging) return;
            Panel panel = AssociatedObject.Parent as Panel;

            //停止拖动
            _isDragging = false;

            //释放鼠标
            _img.ReleaseMouseCapture();

            //解除事件绑定
            _img.MouseMove -= AssociatedObjectMouseMove;
            _img.MouseLeftButtonUp -= AssociatedObjectMouseLeftButtonUp;

            //如果允许移动,则将"影子Transform"的偏移量赋值给"对象的Transform"
            if (IsMovable)
            {
                _elementTranslate.X = _imgTranslate.X;
                _elementTranslate.Y = _imgTranslate.Y;
            }

            //重新初始化偏移量,同时将对象本身恢复原透明度
            _imgTranslate = new TranslateTransform();
            _offset = new Point(0, 0);
            AssociatedObject.Opacity = 1;

            //清除Image
            if (panel != null) panel.Children.Remove(_img);

            //为下次移动准备一个新的Image
            _img = new Image();
        }


        /// 
        /// 开始拖动时触发
        /// 
        /// 
        /// 
        private void AssociatedObjectMouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            _isDragging = true;//处理标志位

            AssociatedObject.Opacity = .35;//将对象透明度降低

            //生成对象的"位图影子副本"
            WriteableBitmap bitmap = new WriteableBitmap(AssociatedObject, new TranslateTransform());
            if (_img == null) return;

            _img.Source = bitmap;
            _img.HorizontalAlignment = HorizontalAlignment.Left;
            _img.VerticalAlignment = VerticalAlignment.Top;
            _img.Stretch = Stretch.None;
            _img.Width = bitmap.PixelWidth;
            _img.Height = bitmap.PixelHeight;

            _imgTranslate.X = _elementTranslate.X;
            _imgTranslate.Y = _elementTranslate.Y;

            _img.RenderTransform = _imgTranslate;

            //注册鼠标事件,以响应拖动
            _img.MouseMove += AssociatedObjectMouseMove;
            _img.MouseLeftButtonUp += AssociatedObjectMouseLeftButtonUp;

            Panel panel = AssociatedObject.Parent as Panel;

            if (panel != null) panel.Children.Add(_img);

            _offset = e.GetPosition(_img);

            //捕获鼠标,以防止鼠标移动过快时,甩掉"影子对象"
            _img.CaptureMouse(); 
        }
    }
}
而且很多时候,对象拖动后要求能保存新的位置信息,以方便用户下次进入时,能自动恢复到上次改变过的位置。
示例代码: Xaml部分


    
    	
    		
    		
    	
    	
    		
    		
    	
    	
    		
                
    			 
    		
    	
        
        
            
        
        
    

示例代码:Xaml.cs部分
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace slApp
{
    public partial class MainPage : UserControl
    {
        Point p;

        public MainPage()
        {
            InitializeComponent();
        }

        private void btnSave_Click(object sender, RoutedEventArgs e)
        {
            TranslateTransform transform = bdr.RenderTransform as TranslateTransform;
            if (transform != null) 
            {
                p.X = transform.X;
                p.Y = transform.Y;
            }
        }

        private void btnLoad_Click(object sender, RoutedEventArgs e)
        {
            TranslateTransform transform = bdr.RenderTransform as TranslateTransform;
            if (transform != null)
            {
                transform.X = p.X;
                transform.Y = p.Y;
            }
        }
    }
}
四、基于MatrixTransform的拖动
Blend自带的MouseDragElementBehavior,其内部原理就是利用MatrixTransform形成的偏移。
示例源码
http://files.cnblogs.com/yjmyzz/DragElementBehaviorSample.zip

你可能感兴趣的:(silverlight:对象拖动的优雅解决方案)