由于在 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. Electron 的解释, 什么是Electron

    https://wizardforcel.gitbooks.io/electron-doc/content/development/build-instructions-windows.html

  2. RedisTemplate Redis 操作

    stringRedisTemplate.opsForValue().set("test", "100",60*10,TimeUnit.SECONDS);//向r ...

  3. JVM7、8详解及优化

    一.引言:永久代为什么被移出HotSpot JVM了? 详见:JEP 122: Remove the Permanent Generation 原因主要有两个: 1.由于Permanent Gener ...

  4. java 用volatile和不用volatile的区别

    在当前的Java内存模型下,线程可以把变量保存在本地内存(比如机器的寄存器)中,而不是直接在主存中进行读写.这就可能造成一个线程在主存中修改了一个变量的值,而另外一个线程还继续使用它在寄存器中的变量值 ...

  5. HttpClient 超时时间

    setSoTimeout(MilSec):连接超时时间.如果在连接过程中有数据传输,超时时间重新计算. setConnectTimeout(MilSec):获取连接超时时间.如果该参数没有设置,那么默 ...

  6. 103041000997维护的是周批,按周合并后再考虑最小采购批量、舍入值、然后回写到SAP系统

    描述:103041000997维护的是周批量,但最终没有按周批量来回写数据. 业务逻辑如下: 1.净需求考虑数量按周汇总(也有按日.按3天,具体 要根据物料主数据维护来判断) 2.第1点的结果再加上安 ...

  7. STL容器的常用用法

    STL: 1.vector: vector<int> v;vector<int> v(10);//定义大小为10的int型向量容器.vector<int> v(10 ...

  8. 132. Palindrome Partitioning II (String; DP)

    Given a string s, partition s such that every substring of the partition is a palindrome. Return the ...

  9. linux的ssh服务

    1.检查是否安装ssh > rpm -qa|grep ssh 2.安装ssh服务 > yum install ssh 配置 /etc/ssh/sshd_config 端口 22 3.启动s ...

  10. 从输入url到显示网页发生了什么

    原文链接:https://juejin.im/post/5bf23afa6fb9a049be5d1494 在浏览器中输入url到显示网页主要包含两个部分: 网络通信和页面渲染 互联网内各网络设备间的通 ...