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

本文介绍了在Silverlight中实现对象拖动的四种方法:基于Canvas绝对定位、Margin值、TranslateTransform偏移量及MatrixTransform。提供了详细的实现代码及保存拖动后位置的方法。
对象拖动是一个老生常谈的话题,在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形成的偏移。
示例源码

转载于:https://www.cnblogs.com/yjmyzz/archive/2011/06/22/2087599.html

### 光流法C++源代码解析与应用 #### 光流法原理 光流法是一种在计算机视觉领域中用于追踪视频序列中运动物体的方法。它基于亮度不变性假设,即场景中的点在时间上保持相同的灰度值,从而通过分析连续帧之间的像素变化来估计运动方向和速度。在数学上,光流场可以表示为像素位置和时间的一阶导数,即Ex、Ey(空间梯度)和Et(时间梯度),它们共同构成光流方程的基础。 #### C++实现细节 在给定的C++源代码片段中,`calculate`函数负责计算光流场。该函数接收一个图像缓冲区`buf`作为输入,并初始化了几个关键变量:`Ex`、`Ey`和`Et`分别代表沿x轴、y轴和时间轴的像素强度变化;`gray1`和`gray2`用于存储当前帧和前一帧的平均灰度值;`u`则表示计算出的光流矢量大小。 #### 图像处理流程 1. **初始化和预处理**:`memset`函数被用来清零`opticalflow`数组,它将保存计算出的光流数据。同时,`output`数组被填充为白色,这通常用于可视化结果。 2. **灰度计算**:对每一像素点进行处理,计算其灰度值。这里采用的是RGB通道平均值的计算方法,将每个像素的R、G、B值相加后除以3,得到一个近似灰度值。此步骤确保了计算过程的鲁棒性和效率。 3. **光流向量计算**:通过比较当前帧和前一帧的灰度值,计算出每个像素点的Ex、Ey和Et值。这里值得注意的是,光流向量的大小`u`是通过`Et`除以`sqrt(Ex^2 + Ey^2)`得到的,再乘以10进行量化处理,以减少计算复杂度。 4. **结果存储与阈值处理**:计算出的光流值被存储在`opticalflow`数组中。如果`u`的绝对值超过10,则认为该点存在显著运动,因此在`output`数组中将对应位置标记为黑色,形成运动区域的可视化效果。 5. **状态更新**:通过`memcpy`函数将当前帧复制到`prevframe`中,为下一次迭代做准备。 #### 扩展应用:Lukas-Kanade算法 除了上述基础的光流计算外,代码还提到了Lukas-Kanade算法的应用。这是一种更高级的光流计算方法,能够提供更精确的运动估计。在`ImgOpticalFlow`函数中,通过调用`cvCalcOpticalFlowLK`函数实现了这一算法,该函数接受前一帧和当前帧的灰度图,以及窗口大小等参数,返回像素级别的光流场信息。 在实际应用中,光流法常用于目标跟踪、运动检测、视频压缩等领域。通过深入理解和优化光流算法,可以进一步提升视频分析的准确性和实时性能。 光流法及其C++实现是计算机视觉领域的一个重要组成部分,通过对连续帧间像素变化的精细分析,能够有效捕捉和理解动态场景中的运动信息
微信小程序作为腾讯推出的一种轻型应用形式,因其便捷性与高效性,已广泛应用于日常生活中。以下为该平台的主要特性及配套资源说明: 特性方面: 操作便捷,即开即用:用户通过微信内搜索或扫描二维码即可直接使用,无需额外下载安装,减少了对手机存储空间的占用,也简化了使用流程。 多端兼容,统一开发:该平台支持在多种操作系统与设备上运行,开发者无需针对不同平台进行重复适配,可在一个统一的环境中完成开发工作。 功能丰富,接口完善:平台提供了多样化的API接口,便于开发者实现如支付功能、用户身份验证及消息通知等多样化需求。 社交整合,传播高效:小程序深度嵌入微信生态,能有效利用社交关系链,促进用户之间的互动与传播。 开发成本低,周期短:相比传统应用程序,小程序的开发投入更少,开发周期更短,有助于企业快速实现产品上线。 资源内容: “微信小程序-项目源码-原生开发框架-含效果截图示例”这一资料包,提供了完整的项目源码,并基于原生开发方式构建,确保了代码的稳定性与可维护性。内容涵盖项目结构、页面设计、功能模块等关键部分,配有详细说明与注释,便于使用者迅速理解并掌握开发方法。此外,还附有多个实际运行效果的截图,帮助用户直观了解功能实现情况,评估其在实际应用中的表现与价值。该资源适用于前端开发人员、技术爱好者及希望拓展业务的机构,具有较高的参考与使用价值。欢迎查阅,助力小程序开发实践。资源来源于网络分享,仅用于学习交流使用,请勿用于商业,如有侵权请联系我删除!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值