locked
Essential Silverlight翻译连载---第五章 续3 RRS feed

  • 常规讨论

  • 拖放

    JavaScript最难实现的效果之一是拖放。不但控制页面上的单个元素非常困难,而且浏览器的不兼容也使开发人员非常头痛。Silverlight并没有内置对拖放的支持,但稍微花点力气是有可能实现它的。如果适当地做些计划,代码可以很快的组织起来。

    拖放一般由三个阶段组成,它们可以直接映射到Silverlight的鼠标事件:

    MouseLeftButtonDown

    用户在可拖动元素上单击鼠标进入拖动状态。

    MouseMove

    用户按住鼠标键不放移动鼠标。根据鼠标指针当前位置改变对象的位置

    MouseLeftButtonUp

    用户释放鼠标,退出拖动状态。

    实际上,这只解决了50%的问题。另外的50%来自于一个不同的挑战。假设被拖动的元素是一个10×10像素的正方形。用户单击了方块的某个地方并拖动它。让我们进一步假设用户在某个地方释放了它,例如在(50,40),也就是说x坐标是50个像素,y坐标是40个像素。JavaScript代码现在将把方块放在什么位置呢?在(50,40?方块的左上角将放置在这个位置上,那么只有当用户当初正确地点击了被拖动方块左上角的位置才算是正确,当然,发生这种情况的可能性很小。

    所以使用绝对位置不是一个好的解决方案。我们应该使用变化量做为替代:用户使鼠标移动了多少距离呢?在用户进行拖放的第一个阶段,JavaScript记录当前鼠标指针的位置。只要鼠标一移动,将会得到鼠标指针的新位置。基于这两个值,JavaScript可以计算出鼠标移动了多少个像素。例如向右移动了15个像素,向下移动了20个像素。把这些变化值赋给那些将被移动的对象,这样它就会向右移动15个像素,向下移动了20个像素。(实际上,这些像素值可能会非常小,因为MouseMove事件会频繁地被触发。)

    这个算法对于拖放的另一半解决方案是必不可少的。写这些代码也不是什么难事,我们从例5-10XAML代码开始。这里再一次使用矩形框,一个黑色实心圆将做为被拖动的对象。注意实心圆是如何使用属性处理三个相关的鼠标事件的。

    5-10 拖放,XAML文件(DragDrop.xaml

    <Canvas xmlns="http://schemas.microsoft.com/client/2007"

            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <Rectangle Width="300" Height="150" Stroke="Orange" StrokeThickness="15" />

    <Ellipse Width="50" Height="50" Fill="Black" Canvas.Left="20" Canvas.Top="20"

               MouseLeftButtonDown="mouseInit"

               MouseLeftButtonUp="mouseRelease"

               MouseMove="mouseMove" />

    </Canvas>

    我 们现在可以开始实现拖放了。在这之前,需要注意一点,是否还记得事件的冒泡机制?如果一个被拖动的对象在执行一个鼠标事件,会把它提交给父元素。更糟糕的 是,对象链中的更多元素也会执行这个事件(这也叫“事件隧道”)。其他元素有可能执行这些事件,这是您所不想看到的。因此Silverlight提供了一个captureMouse()方法。如果一个对象调用了这个方法,所有鼠标事件将直接发送给这个对象,其他对象将再也无法接收它们。当鼠标左键按下(MouseLeftButtonDown事件),被拖动对象的初始位置被记录,鼠标捕获状态被激活。全局变量moving记录了应用程序的拖放状态(truefalse)。

    function mouseInit(sender, eventArgs) {

      sender.captureMouse();

        lastX = eventArgs.getPosition(null).x;

        lastY = eventArgs.getPosition(null).y;

        moving = true;

    }

    如果鼠标正在移动,依照本节开头所设计的算法,被拖动对象的位置信息将被不断地更新。请记住,鼠标开始拖动时的位置已经确定,脚本代码将计算开始位置和最后位置之间的变化值。因而被拖动对象的位置也随之变化。最终,这个变量所控制的坐标值也会被更新。

    如果您需要更新实心圆的位置,将不得不注意一个特殊问题:需要通过更改名称的Canvas.LeftCanvas.Top属性来改变它的位置。但JavaScript不允许在名称后面使用“.”,所以不能使用对象的name.Canvas.Left。您可以转而使用JavaScript的数组语法:objectname[‘Canvas.Left’]。现在您只需要记住事件处理方法的第一个参数是触发事件的对象,在本例中就是被移动的元素。这些代码非常简单:

    function mouseMove(sender, eventArgs) {

       if (moving) {

            var x = eventArgs.getPosition(null).x;

            var y = eventArgs.getPosition(null).y;

            sender['Canvas.Left'] += x - lastX;

            sender['Canvas.Top'] += y - lastY;

            lastX = x;

            lastY = y;

        }

    }

    提示:现在应该知道我们为什么使用moving变量了吧。在任何时间内都会发生鼠标移动事件,但只想在用户拖动实心圆时才使用它!

    最后一个步骤在用户释放鼠标按键时发生(LeftMouseButtonUp事件)。您可以把moving变量重新设置为false,并通过调用releaseMouseCapture()方法来解除页面上的其他元素对鼠标事件的访问限制。我们将在这实现一个附加功能。正如您所见,XAML文件上存在一个橙色的矩形。它将成为被拖动元素的屏障:元素不能碰到或离开这个边界。

    我们需要定义几个变量用于确定实心圆位置的最大值和最小值:

    var minX = 15;

    var maxX = 235;

    var minY = 15;

    var maxY = 85;

    当用户开始拖放操作时,我们将保存实心圆的位置(这些代码很明显属于mouseInit()方法):

    startX = sender['Canvas.Left'];

    startY = sender['Canvas.Top'];

    最后,当用户释放鼠标按键时,开始位置已经确定,并检查坐标值是否有效。如果坐标值超出边界,被拖动对象则被放置于开始拖动时的位置。

    var x = sender['Canvas.Left'];

    var y = sender['Canvas.Top'];

    if (x < minX || x> maxX || y < minY || y > maxY) {

        sender['Canvas.Left'] = startX;

        sender['Canvas.Top'] = startY;

    }

    5-11是完整的JavaScript代码。图5-5显示了其效果:现在如果用户释放鼠标按钮,实心圆将跳回它的初始位置。

    5-11 拖放,XAML JavaScript文件(DragDrop.xaml.js

    var startX, startY, lastX, lastY;

    var minX = 15;

    var maxX = 235;

    var minY = 15;

    var maxY = 85;

    var moving = false;

    function mouseInit(sender, eventArgs) {

        sender.captureMouse();

        startX = sender['Canvas.Left'];

        startY = sender['Canvas.Top'];

        lastX = eventArgs.getPosition(null).x;

        lastY = eventArgs.getPosition(null).y;

        moving = true;

    }

    function mouseRelease(sender, eventArgs) {

        sender.releaseMouseCapture();

        moving = false;

        var x = sender['Canvas.Left'];

      var y = sender['Canvas.Top'];

        if (x < minX || x> maxX || y < minY || y > maxY) {

            sender['Canvas.Left'] = startX;

            sender['Canvas.Top'] = startY;

        }

    }

    function mouseMove(sender, eventArgs) {

        if (moving) {

            var x = eventArgs.getPosition(null).x;

            var y = eventArgs.getPosition(null).y;

            sender['Canvas.Left'] += x - lastX;

            sender['Canvas.Top'] += y - lastY;

            lastX = x;

            lastY = y;

        }

    }


    My blog: http://blog.csdn.net/dotfun http://dotfun.cnblogs.com

    My contact: QQ:372900288 E-mail:372900288@qq.com msn:sellnet007@hotmail.com

    2009年3月11日 8:20