由于在 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. spring 每个jar的作用

    spring.jar 是包含有完整发布模块的单个jar 包.但是不包括mock.jar, aspects.jar, spring-portlet.jar, and spring-hibernate2. ...

  2. Windows phone 自定义控件(无外观控件)——ColorPicker

    编码前 在上一篇博客中,写的是一个UserControl的子类,它具有固定的外观(虽然也可以通过样式来进行修改,但受到的限制很大).如果你想要使用这个控件的逻辑,但是希望在使用的时候可以更改控件的外观 ...

  3. C++ volatile

    volatile的位置与const相同——都是作为类型的附加修饰符 使用volatile的主要目的是提示编译器该对象的值可能在编辑器未监测的情况下被改变,因此编译器不能武断地对引用这些对象的代码作优化 ...

  4. UVa 230 Borrowers(map和set)

    I mean your borrowers of books - those mutilators of collections, spoilers of the symmetry of shelve ...

  5. JS 图片切换

    <%@ Page Language="C#" AutoEventWireup="true" CodeFile="zzzz.aspx.cs&quo ...

  6. win 下 apache 虚拟主机配置方式

    虚拟主机的配置在apache安装目录下/conf/extra/httpd-vhosts.conf文件中,需要在/conf/httpd.conf中开启. LoadModule vhost_alias_m ...

  7. Spring整合Struts2框架的第一种方式(Action由Struts2框架来创建)。在我的上一篇博文中介绍的通过web工厂的方式获取servcie的方法因为太麻烦,所以开发的时候不会使用。

    1. spring整合struts的基本操作见我的上一篇博文:https://www.cnblogs.com/wyhluckdog/p/10140588.html,这里面将spring与struts2 ...

  8. [Usaco2009 Dec]Toll 过路费

    题面: 跟所有人一样,农夫约翰以着宁教我负天下牛,休教天下牛负我(原文:宁我负人,休教人负我)的伟大精神,日日夜夜苦思生财之道.为了发财,他设置了一系列的规章制度,使得任何一只奶牛在农场中的道路行走, ...

  9. Spring AOP配置

    相关概念有点拗口,我这里简单总结一个,切面,决定做什么,写处理逻辑,比如打日志.切入点,决定在哪些方里拦截,一般填正则表达式查询. 通知,就是连接切面和切入点的桥梁. 其中遇到了配置好,启动服务器没报 ...

  10. 2018.10.13 bzo1934: [Shoi2007]Vote 善意的投票(最小割)

    传送门 最小割定义题. 按照题意建边就行了. 考虑把冲突变成把aaa选入不与自己匹配的集合所需要付出的代价. 然后跑最小割就行了. 代码: #include<bits/stdc++.h> ...