zoukankan      html  css  js  c++  java
  • silverlight:对象拖动的优雅解决方案

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

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

    二、基于对象Margin值的拖动
    Margin是对象的通用属性,通过改变Margin值理论上可在任何布局下,重新定位对象的位置。
    缺点就是算法处理有些小复杂,初次看着有点晕。
    三、基于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<FrameworkElement>
        {
            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();
    
            /// <summary>
            /// Drag行为附加到对象上时触发
            /// </summary>
            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;
            }
    
            /// <summary>
            /// Drag行为从对象剥离时触发
            /// </summary>
            protected override void OnDetaching()
            {
                base.OnDetaching();
                //移除鼠标左键事件处理
                AssociatedObject.MouseLeftButtonDown -= AssociatedObjectMouseLeftButtonDown;
            }
    
            /// <summary>
            /// 动象拖动时的处理
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            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);
            }
    
            /// <summary>
            /// 托运结束时的处理
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            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();
            }
    
    
            /// <summary>
            /// 开始拖动时触发
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            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部分
    <UserControl
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
                 xmlns:ctl="clr-namespace:SLControls;assembly=SlControls"
        xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" x:Class="slApp.MainPage"
        mc:Ignorable="d"
        d:DesignHeight="300" d:DesignWidth="400">
    
        <Grid x:Name="LayoutRoot" Background="White">
        	<Grid.RowDefinitions>
        		<RowDefinition Height="*"/>
        		<RowDefinition Height="*"/>
        	</Grid.RowDefinitions>
        	<Grid.ColumnDefinitions>
        		<ColumnDefinition Width="*"/>
        		<ColumnDefinition Width="*"/>
        	</Grid.ColumnDefinitions>
        	<Border x:Name="bdr" Cursor="Hand" BorderBrush="#FFC2B529" BorderThickness="10" Background="#FF291313" HorizontalAlignment="Center" 
    
    VerticalAlignment="Center" Width="50" Height="50">
        		<i:Interaction.Behaviors>
                    <!--一行代码就搞定了拖动!-->
        			<ctl:Drag IsMovable="True"/> 
        		</i:Interaction.Behaviors>
        	</Border>
            
            <StackPanel Orientation="Horizontal" Grid.Row="1" Grid.ColumnSpan="2" VerticalAlignment="Center" HorizontalAlignment="Center">
                <Button x:Name="btnSave" Click="btnSave_Click" Grid.Column="1" Padding="10,5">保存当前位置</Button>
            <Button x:Name="btnLoad" Click="btnLoad_Click" Grid.Row="1" Grid.Column="1" Margin="10,0,0,0" Padding="10,5">加载上次位置</Button>
            </StackPanel>
        </Grid>
    </UserControl>
    
    示例代码: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形成的偏移。
    示例源码
  • 相关阅读:
    ffmpeg 转换VC工具已经可以生成工程文件
    ffmpeg 转换VC工具已经可以生成工程文件(续)
    ffmpeg 转换VC工具 V1.1.1
    ffmpeg 转换VC工具已经可以生成工程文件(续)
    ffmpeg 工程代码半自动转换vc工具
    ffmpeg 转换VC工具 V1.1.1
    lua 解析ffmpeg结构体时候用的正则表达式
    ffmpeg 工程代码半自动转换vc工具
    lua 解析ffmpeg结构体时候用的正则表达式
    ffmpeg 转换VC工具已经可以生成工程文件
  • 原文地址:https://www.cnblogs.com/yjmyzz/p/2087599.html
Copyright © 2011-2022 走看看