由于在 xaml 体系中,控件没有传统 WebForm 中的 Left、Top、Right、Bottom 这些属性,取而代之的是按比例(像 Grid)等等的响应布局。但是,传统的这些设置 Left、Top 的硬编码的需求仍然存在,所以,在所有的 xaml 体系中,均存在一个代替的控件——Canvas。本文基于 Canvas 来实现控件的拖拉效果。

在整个控件拖拉的过程当中,可以分解为 3 个部分,第一个部分是输入设备点击控件,第二个部分是保持按下的状态下移动输入设备,第三个部分是释放输入设备。那么,就必须对这 3 个过程做出相应的逻辑处理。

那么,有一个重要的问题来了,如何标识控件是否处于拖拉状态。C# 不像 javascript 那样,能够随便修改对象,添加属性。但是,这难不倒微软的工程师,在 xaml 体系中,有附加属性这样一个东西。

 public static class DragHelper
{
public static readonly DependencyProperty IsPraggingProperty = DependencyProperty.RegisterAttached("IsDragging", typeof(bool), typeof(DragHelper), new PropertyMetadata(false));
}

这里使用 IsDragging 来标识控件是否处于拖放过程中。

然后接下来我们需要一个初始化的方法来使控件可以拖放。

public static class DragHelper
{
public static bool Dragable(this UIElement control)
{
// TODO
}
}

这里使用扩展方法,使调用方简洁一些。该方法返回一个布尔值,指示是否操作成功。对于控件的父对象为 Canvas 的,我们返回 true,否则返回 false。

public static class DragHelper
{
public static bool Dragable(this UIElement control)
{
if(control==null)
{
throw new ArgumentNullException("control");
}
if(VisualTreeHelper.GetParent(control) is Canvas)
{
// TODO
return true;
}
else
{
return false;
}
}
}

由于控件没有所谓的 Parent 属性,因此我们需要使用可视树来获取父控件。

接下来将一开始的 3 个过程映射到相应的对象事件。

1、输入设备点击控件:UIElement.PointertPressed

2、输入设备移动:Window.Current.CoreWindow.PointerMoved

3、释放输入设备:Window.Current.CoreWindow.PointerReleased

public static bool Dragable(this UIElement control)
{
// null 判断,参考上面
if(VisualTreeHelper.GetParent(control) is Canvas)
{
control.PointerPressed+=(sender,e)=>
{
// 设置控件进入拖放状态。
control.SetValue(IsDraggingProperty, true);
// TODO
};
var coreWindow = Window.Current.CoreWindow;
coreWindow.PointerMoved+=(sender,args)=>
{
if((bool)control.GetValue(IsDraggingProperty))
{
// TODO
}
};
coreWindow.PointerReleased+=(sender,args)=>
{
// TODO
}; return true;
}
else
{
return false;
}
}

接下来,在移动的过程中,我们需要不断设置控件的 Left、Top 这两个 Canvas 的附加属性来达到拖放的效果。可以通过

args.CurrentPoint.Position

来获得输入设备当前的位置。那么每一次设置的位置就等于初始位置加上当次位置。

总体代码:

     public static class DragHelper
{
public static readonly DependencyProperty IsDraggingProperty = DependencyProperty.RegisterAttached(
"IsDragging", typeof(bool), typeof(DragHelper), new PropertyMetadata(false)); public static readonly DependencyProperty StartLeftProperty = DependencyProperty.RegisterAttached("StartLeft",
typeof(double), typeof(DragHelper), new PropertyMetadata(0.0d)); public static readonly DependencyProperty StartTopProperty = DependencyProperty.RegisterAttached("StartTop",
typeof(double), typeof(DragHelper), new PropertyMetadata(0.0d)); public static readonly DependencyProperty StartPositionProperty =
DependencyProperty.RegisterAttached("StartPosition", typeof(Point), typeof(DragHelper),
new PropertyMetadata(default(Point))); public static bool Dragable(this UIElement control)
{
if (control == null)
{
throw new ArgumentNullException("control");
}
if (VisualTreeHelper.GetParent(control) is Canvas)
{
control.PointerPressed += (sender, e) =>
{
control.SetValue(IsDraggingProperty, true);
control.SetValue(StartLeftProperty, Canvas.GetLeft(control));
control.SetValue(StartTopProperty, Canvas.GetTop(control));
control.SetValue(StartPositionProperty, e.GetCurrentPoint(null).Position);
};
var coreWindow = Window.Current.CoreWindow;
coreWindow.PointerMoved += (sender, args) =>
{
if ((bool)control.GetValue(IsDraggingProperty))
{
var currentPosition = args.CurrentPoint.Position;
var startPosition = (Point)control.GetValue(StartPositionProperty);
var deltaX = currentPosition.X - startPosition.X;
var deltaY = currentPosition.Y - startPosition.Y;
var startLeft = (double)control.GetValue(StartLeftProperty);
var startTop = (double)control.GetValue(StartTopProperty);
Canvas.SetLeft(control, startLeft + deltaX);
Canvas.SetTop(control, startTop + deltaY);
}
};
coreWindow.PointerReleased += (sender, args) => control.SetValue(IsDraggingProperty, false); return true;
}
else
{
return false;
}
}
}
}

在第一步保存控件相关信息到附加属性当中便于移动状态使用。

效果:

应用:

例如开发一个 ListView 返回顶部的小插件。

由于 Button 会吞掉 PointerPressed 这个事件,因此这里使用了 Border 来模拟。

Border 相关的 xaml 代码:

             <Canvas>
<Border x:Name="btn"
Canvas.Left="300"
Canvas.Top="400"
Width="50"
Height="50"
CornerRadius="50"
BorderBrush="Gray"
BorderThickness="3"
Background="Red"
Tapped="Btn_OnTapped">
<TextBlock Text="顶"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="25"
Foreground="Gold" />
</Border>
</Canvas>

ListView 滚回到顶部的代码。

        private void Btn_OnTapped(object sender, TappedRoutedEventArgs e)
{
// Lvw 为 ListView 控件。
var item = Lvw.Items.FirstOrDefault();
if (item != null)
{
Lvw.ScrollIntoView(item);
}
}

【WinRT】让控件飞,WinRT 中实现 web 中的 dragable 效果的更多相关文章

  1. ASP.NET中共有哪几种类型的控件?其中,HTML控件、HTML服务器控件和WEB服务器控件之间有什么区别

    ASP.NET的控件包括WEB服务器控件.WEB用户控件.WEB自定义控件.HTML服务器控件和HTML控件.HTML控件.HTML服务器控件和WEB服务器控件之间的区别如下所示.q      HTM ...

  2. 背水一战 Windows 10 (65) - 控件(WebView): 对 WebView 中的内容截图, 通过 Share Contract 分享 WebView 中的被选中的内容

    [源码下载] 背水一战 Windows 10 (65) - 控件(WebView): 对 WebView 中的内容截图, 通过 Share Contract 分享 WebView 中的被选中的内容 作 ...

  3. WPF中Popup控件在Win7以及Win10等中的对齐点方式不一样的解决方案 - 简书

    原文:WPF中Popup控件在Win7以及Win10等中的对齐点方式不一样的解决方案 - 简书 最近项目中使用弹出控件Popup,发现弹出框的对齐方式在不同的系统中存在不同(Popup在win10上是 ...

  4. 客户端的javascript改变了asp.net webform页面控件的值,后台代码中如何获取修改后的值。

    客户端的javascript改变了asp.net webform页面控件的值,后台代码中如何获取修改后的值.     无论是什么的html控件,只要加上了runat="server" ...

  5. WPF封装控件时 检测是否在设计模式中

    原文:WPF封装控件时 检测是否在设计模式中 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/Vblegend_2013/article/detail ...

  6. 安卓,网页控件,显示网页 Android, web controls, display web pages

    安卓,网页控件,显示网页Android, web controls, display web pages 作者:韩梦飞沙 Author:han_meng_fei_sha 邮箱:313134555@qq ...

  7. wpf的UserControl用户控件怎么添加到Window窗体中

    转载自 http://www.cnblogs.com/shuang121/archive/2013/01/09/2853591.html 我们来新建一个用户控件UserControl1.xaml &l ...

  8. 解决Select2控件不能在jQuery UI Dialog中不能搜索的bug

    本文使用博客园Markdown编辑器进行编辑 1.问题呈现 项目中使用了jQuery UI的Dialog控件,一般用来处理需要提示用户输入或操作的简单页面.逻辑是修改一个广告的图片和标题. 效果截图如 ...

  9. ActiveX控件打包成Cab置于网页中自动下载安装(转载)

    原文出自http://www.iteye.com/topic/110834 [背景] 做过ActiveX控件的朋友都知道,要想把自己做的ActiveX控件功能放在自己的网页上使用,那么用户在客户端就必 ...

随机推荐

  1. appcan更新

    升级用到了config.xml文件中配置的‘更新地址‘所填写的url,此url开发者可任意配置自己的服务器地址* 当app执行uexWidget.checkUpdate()时,AppCan会请求上述u ...

  2. xpath的层级与逻辑定位

    xpath的层级与逻辑定位: 之前我们是通过class和id,name,如果我们所需要的元素没有class,id,name这样的元素,怎么定位呢 1.在不使用xpath情况下:元素没有属性值得时候怎么 ...

  3. 基于Woodstox的StAX 2 (Streaming API for XML)解析XML

    StAX (Streaming API for XML)面向流的拉式解析XML,速度快.占用资源少,非常合适处理大数据量的xml文件. 详细教程和说明可以参见以下几篇文章: 使用 StAX 解析 XM ...

  4. Princess Principal(思维题)

    Princess Principal https://www.nowcoder.com/acm/contest/201/J 题目描述 阿尔比恩王国(the Albion Kingdom)潜伏着一群代号 ...

  5. 判断Javascript对象是否为空

    判断普通javascript对象是否为空(含有可枚举的属性,自有的.继承的都可以),可使用jQuery 3.2.1版的isEmptyObject()方法: isEmptyObject: functio ...

  6. python版本安装

    目的 本文目的在于,对于不熟悉Python的人,教你: 1. 从哪里找到 可以下载到 各种版本的 包括Python 2.x和Python 3.x的 最新版本的 Python. 高手请无视之. 2.以及 ...

  7. [leetcode]29. Divide Two Integers 两整数相除

    Given two integers dividend and divisor, divide two integers without using multiplication, division ...

  8. 24-filter-拦截器

    在web.xml里面配置,有两种形式: 然后编写filter类: @Override public void doFilter(ServletRequest arg0, ServletResponse ...

  9. centos7下源码安装mysql5.7.16

    一.下载源码包下载mysql源码包 http://mirrors.sohu.com/mysql/MySQL-5.7/mysql-5.7.16.tar.gz 二.安装约定: 用户名:mysql 安装目录 ...

  10. HTML5新协议介绍 WebSocket

    WebSocket protocol 是HTML5一种新的协议(protocol).它是实现了浏览器与服务器全双工通信(full-duplex). 现在,很多网站为了实现即时通讯(real-time) ...