使用扩展方法对代码的行为进行封装的例子:封装UIElement的“拖动”
很多情况下,我们需要对界面上的元素进行拖动,用鼠标在VS中biaji,biaji,biaji,点几个事件,然后再写出一堆代码,浪费时间不说,由IDE自动生成的那些代码实在是太难看,影响心情。本文使用扩展方法,对于这类行为需要进行封装,以使代码更简单简洁。
封装原则如下:
(1)要简单,最好是一行代码就搞定;
(2)要强大,能用于尽量多的类;
(3)要灵活,可适用于尽量多的场景。
在本文的末尾添加了修改版,修改版代码更简洁,操作更简单,且可以设置多个拖动逻辑。
====
设计下面的扩展方法原型:
public static void SetDraggable(this UIElement element, IInputElement relativeTo, Action<DraggableContext> moveCallback = null, Action<DraggableContext> beforeDragCallback = null, Action<DraggableContext> afterDragCallback = null)
element 是拖动的界面元素,relativeTo 是参照物,moveCallback 是移动过程中的回调方法。DraggableContext 是一个类,包装了调用方所需要的信息。beforeDragCallback 是拖动前的处理,假设要拖动一个TextBlock,拖动前可以将它改变颜色,以示醒目。afterDragCallback 是拖动结束后的处理。
DraggableContext 的代码为:
public class DraggableContext
{
public DependencyObject Owner { get; private set; }
public IInputElement RelativeTo { get; private set; }
public Point StartPoint { get; internal set; }
public Point EndPoint { get; internal set; }public Point Offset
{
get { return new Point { X = EndPoint.X - StartPoint.X, Y = EndPoint.Y - StartPoint.Y }; }
}private Boolean _dragging;
public Boolean Dragging
{
get { return _dragging; }
internal set
{
if (value != _dragging)
{
_dragging = value;
if (value == true)
{
if (BeforeDragCallback != null)
BeforeDragCallback(this);
}
else
{
if (AfterDragCallback != null)
AfterDragCallback(this);
}
}
}
}public Action<DraggableContext> MoveCallback { get; private set; }
public Action<DraggableContext> BeforeDragCallback { get; private set; }
public Action<DraggableContext> AfterDragCallback { get; private set; }
public DraggableContext(DependencyObject owner, IInputElement relativeTo, Action<DraggableContext> moveCallback = null, Action<DraggableContext> beforeDragCallback = null, Action<DraggableContext> afterDragCallback = null)
{
Owner = owner;
RelativeTo = relativeTo;
MoveCallback = moveCallback;
BeforeDragCallback = beforeDragCallback;
AfterDragCallback = afterDragCallback;
}public override int GetHashCode()
{
return Owner.GetHashCode();
}
}
然后,还需要一个Dictionary,来储存所有的调用者:
private static Dictionary<Object, DraggableContext> _dicDragContext = new Dictionary<Object, DraggableContext>();
然后,是对拖动逻辑的实现:
public static void SetDraggable(this UIElement element, IInputElement relativeTo, Action<DraggableContext> moveCallback = null, Action<DraggableContext> beforeDragCallback = null, Action<DraggableContext> afterDragCallback = null)
{
if (element == null) throw new ArgumentNullException("element");_dicDragContext[element] = new DraggableContext(element, relativeTo, moveCallback, beforeDragCallback, afterDragCallback);
element.MouseLeftButtonDown += new System.Windows.Input.MouseButtonEventHandler(element_MouseLeftButtonDown);
element.MouseLeftButtonUp += new System.Windows.Input.MouseButtonEventHandler(element_MouseLeftButtonUp);
element.MouseLeave += new System.Windows.Input.MouseEventHandler(element_MouseLeave);
element.MouseMove += new System.Windows.Input.MouseEventHandler(element_MouseMove);
}private static void element_MouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
_dicDragContext[sender].Dragging = false;
}private static void element_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
DraggableContext ctx = _dicDragContext[sender];
ctx.Dragging = true;
ctx.StartPoint = e.GetPosition(ctx.RelativeTo);
ctx.EndPoint = ctx.StartPoint;
}private static void element_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e)
{
_dicDragContext[sender].Dragging = false;
}private static void element_MouseMove(object sender, System.Windows.Input.MouseEventArgs e)
{
DraggableContext ctx = _dicDragContext[sender];
if (ctx.Dragging == true)
{
ctx.Dragging = true;
ctx.EndPoint = e.GetPosition(ctx.RelativeTo);
if (ctx.MoveCallback != null)
{
ctx.MoveCallback(ctx);
}
ctx.StartPoint = ctx.EndPoint;
}
}
最后,还需要提供一个扩展方法清除对对象和事件的引用,以避免出现内存泄漏。这个方法需要显示调用。
public static void UnsetDraggable(this UIElement element)
{
element.MouseLeftButtonDown -= new System.Windows.Input.MouseButtonEventHandler(element_MouseLeftButtonDown);
element.MouseLeftButtonUp -= new System.Windows.Input.MouseButtonEventHandler(element_MouseLeftButtonUp);
element.MouseLeave -= new System.Windows.Input.MouseEventHandler(element_MouseLeave);
element.MouseMove -= new System.Windows.Input.MouseEventHandler(element_MouseMove);if (_dicDragContext.ContainsKey(element)) _dicDragContext.Remove(element);
}
完整代码如下:
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.Windows;
6 using System.Windows.Controls;
7
8 namespace Orc.Util
9 {
10 public class DraggableContext
11 {
12 public DependencyObject Owner { get ; private set ; }
13 public IInputElement RelativeTo { get ; private set ; }
14 public Point StartPoint { get ; internal set ; }
15 public Point EndPoint { get ; internal set ; }
16
17 public Point Offset
18 {
19 get { return new Point { X = EndPoint.X - StartPoint.X, Y = EndPoint.Y - StartPoint.Y }; }
20 }
21
22 private Boolean _dragging;
23
24 public Boolean Dragging
25 {
26 get { return _dragging; }
27 internal set
28 {
29 if (value != _dragging)
30 {
31 _dragging = value;
32 if (value == true )
33 {
34 if (BeforeDragCallback != null )
35 BeforeDragCallback( this );
36 }
37 else
38 {
39 if (AfterDragCallback != null )
40 AfterDragCallback( this );
41 }
42 }
43 }
44 }
45
46 public Action < DraggableContext > MoveCallback { get ; private set ; }
47
48 public Action < DraggableContext > BeforeDragCallback { get ; private set ; }
49
50 public Action < DraggableContext > AfterDragCallback { get ; private set ; }
51
52 public DraggableContext(DependencyObject owner, IInputElement relativeTo, Action < DraggableContext > moveCallback = null , Action < DraggableContext > beforeDragCallback = null , Action < DraggableContext > afterDragCallback = null )
53 {
54 Owner = owner;
55 RelativeTo = relativeTo;
56 MoveCallback = moveCallback;
57 BeforeDragCallback = beforeDragCallback;
58 AfterDragCallback = afterDragCallback;
59 }
60
61 public override int GetHashCode()
62 {
63 return Owner.GetHashCode();
64 }
65
66 public static void MoveOnCanvas(DraggableContext ctx)
67 {
68 Canvas cvs = ctx.RelativeTo as Canvas;
69 if (cvs == null ) throw new NotSupportedException( " RelativeTo 必须是 Canvas " );
70 ctx.Owner.SetValue(Canvas.TopProperty, ( double )(ctx.Owner.GetValue(Canvas.TopProperty)) + ctx.Offset.X);
71 ctx.Owner.SetValue(Canvas.LeftProperty, ( double )(ctx.Owner.GetValue(Canvas.LeftProperty)) + ctx.Offset.Y);
72 }
73 }
74
75 public static class ClassHelper
76 {
77 private static Dictionary < Object, DraggableContext > _dicDragContext = new Dictionary < Object, DraggableContext > ();
78
79 public static void SetDraggable( this UIElement element, IInputElement relativeTo, Action < DraggableContext > moveCallback = null , Action < DraggableContext > beforeDragCallback = null , Action < DraggableContext > afterDragCallback = null )
80 {
81 if (element == null ) throw new ArgumentNullException( " element " );
82
83 _dicDragContext[element] = new DraggableContext(element, relativeTo, moveCallback, beforeDragCallback, afterDragCallback);
84 element.MouseLeftButtonDown += new System.Windows.Input.MouseButtonEventHandler(element_MouseLeftButtonDown);
85 element.MouseLeftButtonUp += new System.Windows.Input.MouseButtonEventHandler(element_MouseLeftButtonUp);
86 element.MouseLeave += new System.Windows.Input.MouseEventHandler(element_MouseLeave);
87 element.MouseMove += new System.Windows.Input.MouseEventHandler(element_MouseMove);
88 }
89
90 private static void element_MouseLeftButtonUp( object sender, System.Windows.Input.MouseButtonEventArgs e)
91 {
92 _dicDragContext[sender].Dragging = false ;
93 }
94
95 private static void element_MouseLeftButtonDown( object sender, System.Windows.Input.MouseButtonEventArgs e)
96 {
97 DraggableContext ctx = _dicDragContext[sender];
98 ctx.Dragging = true ;
99 ctx.StartPoint = e.GetPosition(ctx.RelativeTo);
100 ctx.EndPoint = ctx.StartPoint;
101 }
102
103 private static void element_MouseLeave( object sender, System.Windows.Input.MouseEventArgs e)
104 {
105 _dicDragContext[sender].Dragging = false ;
106 }
107
108 private static void element_MouseMove( object sender, System.Windows.Input.MouseEventArgs e)
109 {
110 DraggableContext ctx = _dicDragContext[sender];
111 if (ctx.Dragging == true )
112 {
113 ctx.Dragging = true ;
114 ctx.EndPoint = e.GetPosition(ctx.RelativeTo);
115 if (ctx.MoveCallback != null )
116 {
117 ctx.MoveCallback(ctx);
118 }
119 ctx.StartPoint = ctx.EndPoint;
120 }
121 }
122
123 public static void UnsetDraggable( this UIElement element)
124 {
125 element.MouseLeftButtonDown -= new System.Windows.Input.MouseButtonEventHandler(element_MouseLeftButtonDown);
126 element.MouseLeftButtonUp -= new System.Windows.Input.MouseButtonEventHandler(element_MouseLeftButtonUp);
127 element.MouseLeave -= new System.Windows.Input.MouseEventHandler(element_MouseLeave);
128 element.MouseMove -= new System.Windows.Input.MouseEventHandler(element_MouseMove);
129
130 if (_dicDragContext.ContainsKey(element)) _dicDragContext.Remove(element);
131 }
132 }
133 }
134
====
下面,通过两个案例,看一下这样的封装能带来什么好处。
案例1:在一个ScrollViewer[scroll]中有1副Image[imgMain],当这个图像过大时,需要支持拖动,拖动时,鼠标由箭头变成手的样子,拖动完毕再变回来。界面见下图:
代码如下:
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
this.imgMain.SetDraggable(this, this.Scroll, UseHandCursor, UseArrowCursor);
}private void UseHandCursor(DraggableContext ctx)
{
(ctx.Owner as FrameworkElement).Cursor = Cursors.Hand;
}private void UseArrowCursor(DraggableContext ctx)
{
(ctx.Owner as FrameworkElement).Cursor = Cursors.Arrow;
}private void Scroll(DraggableContext ctx)
{
this.scroll.ScrollToHorizontalOffset(this.scroll.HorizontalOffset - ctx.Offset.X);
this.scroll.ScrollToVerticalOffset(this.scroll.VerticalOffset - ctx.Offset.Y);
}
如果使用 Lambda 表达式,则更简单。
案例2:还是和上面类似场景,在一个Canvas[cvsFont]上有很多文字(TextBlock[tb]),有的文字压着线了,需要手动拖好。当选中文字时,文字显示红色,拖动完毕,变成黑色。
相关代码为:
tb.SetDraggable(this.cvsFont,
(DraggableContext ctx) =>
{
FontContext pos = tb.DataContext as FontContext;
pos.X += ctx.Offset.X / ImageScale;
pos.Y += ctx.Offset.Y / ImageScale;
tb.SetValue(Canvas.TopProperty, pos.Y * ImageScale);
tb.SetValue(Canvas.LeftProperty, pos.X * ImageScale);
},
(DraggableContext ctx) =>
{
tb.Foreground = new SolidColorBrush(Colors.Red);
},
(DraggableContext ctx) =>
{
tb.Foreground = new SolidColorBrush(Colors.Black);
}
);
FontContext 存储了当缩放比为1时,tb 相对 cvsFont 的位置。这些TextBlock的生命周期比较短,它们排队领盒饭时,需要Unset一下:
tb.UnsetDraggable();
====
可以将常用的场景封装成方法,比如说,在DraggableContext类中添加下面的回调方法,将元素在Canvas上移动的逻辑抽象出来:
public static void MoveOnCanvas(DraggableContext ctx)
{
Canvas cvs = ctx.RelativeTo as Canvas;
if (cvs == null) throw new NotSupportedException("RelativeTo 必须是 Canvas");
ctx.Owner.SetValue(Canvas.TopProperty, (double)(ctx.Owner.GetValue(Canvas.TopProperty)) + ctx.Offset.X);
ctx.Owner.SetValue(Canvas.LeftProperty, (double)(ctx.Owner.GetValue(Canvas.LeftProperty)) + ctx.Offset.Y);
}
如果一个元素放置在Canvas上,且只需要支持拖动,不需要其它的逻辑,则一句话就搞定了:
xxx.SetDraggable(cvsXXX,DraggableContext.MoveOnCanvas);
====
2010年11月13日对代码进行修改,修改后的代码更简洁,使用更简单,不用手动Unset,且可以挂接多个逻辑。
代码如下:
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.Windows;
6 using System.Windows.Controls;
7
8 namespace Orc.Util
9 {
10 public class DraggableContext
11 {
12 public DependencyObject Owner { get ; private set ; }
13 public IInputElement RelativeTo { get ; private set ; }
14 public Point StartPoint { get ; internal set ; }
15 public Point EndPoint { get ; internal set ; }
16
17 public Point Offset
18 {
19 get { return new Point { X = EndPoint.X - StartPoint.X, Y = EndPoint.Y - StartPoint.Y }; }
20 }
21
22 private Boolean _dragging;
23
24 public Boolean Dragging
25 {
26 get { return _dragging; }
27 internal set
28 {
29 if (value != _dragging)
30 {
31 _dragging = value;
32 if (value == true )
33 {
34 if (BeforeDragCallback != null )
35 BeforeDragCallback( this );
36 }
37 else
38 {
39 if (AfterDragCallback != null )
40 AfterDragCallback( this );
41 }
42 }
43 }
44 }
45
46 internal void element_MouseLeftButtonUp( object sender, System.Windows.Input.MouseButtonEventArgs e)
47 {
48 this .Dragging = false ;
49 }
50
51 internal void element_MouseLeftButtonDown( object sender, System.Windows.Input.MouseButtonEventArgs e)
52 {
53 this .Dragging = true ;
54 this .StartPoint = e.GetPosition( this .RelativeTo);
55 this .EndPoint = this .StartPoint;
56 }
57
58 internal void element_MouseLeave( object sender, System.Windows.Input.MouseEventArgs e)
59 {
60 this .Dragging = false ;
61 }
62
63 internal void element_MouseMove( object sender, System.Windows.Input.MouseEventArgs e)
64 {
65 if ( this .Dragging == true )
66 {
67 this .Dragging = true ;
68 this .EndPoint = e.GetPosition( this .RelativeTo);
69 if ( this .MoveCallback != null )
70 {
71 this .MoveCallback( this );
72 }
73 this .StartPoint = this .EndPoint;
74 }
75 }
76
77 public Action < DraggableContext > MoveCallback { get ; private set ; }
78
79 public Action < DraggableContext > BeforeDragCallback { get ; private set ; }
80
81 public Action < DraggableContext > AfterDragCallback { get ; private set ; }
82
83 public DraggableContext(DependencyObject owner, IInputElement relativeTo, Action < DraggableContext > moveCallback = null , Action < DraggableContext > beforeDragCallback = null , Action < DraggableContext > afterDragCallback = null )
84 {
85 Owner = owner;
86 RelativeTo = relativeTo;
87 MoveCallback = moveCallback;
88 BeforeDragCallback = beforeDragCallback;
89 AfterDragCallback = afterDragCallback;
90 }
91
92 public override int GetHashCode()
93 {
94 return Owner.GetHashCode();
95 }
96
97 public static void MoveOnCanvas(DraggableContext ctx)
98 {
99 Canvas cvs = ctx.RelativeTo as Canvas;
100 if (cvs == null ) throw new NotSupportedException( " RelativeTo 必须是 Canvas " );
101 ctx.Owner.SetValue(Canvas.TopProperty, ( double )(ctx.Owner.GetValue(Canvas.TopProperty)) + ctx.Offset.X);
102 ctx.Owner.SetValue(Canvas.LeftProperty, ( double )(ctx.Owner.GetValue(Canvas.LeftProperty)) + ctx.Offset.Y);
103 }
104 }
105
106 public static class ClassHelper
107 {
108 public static void SetDraggable( this UIElement element, IInputElement relativeTo, Action < DraggableContext > moveCallback = null , Action < DraggableContext > beforeDragCallback = null , Action < DraggableContext > afterDragCallback = null )
109 {
110 if (element == null ) throw new ArgumentNullException( " element " );
111 DraggableContext ctx = new DraggableContext(element, relativeTo, moveCallback, beforeDragCallback, afterDragCallback);
112 element.MouseLeftButtonDown += new System.Windows.Input.MouseButtonEventHandler(ctx.element_MouseLeftButtonDown);
113 element.MouseLeftButtonUp += new System.Windows.Input.MouseButtonEventHandler(ctx.element_MouseLeftButtonUp);
114 element.MouseLeave += new System.Windows.Input.MouseEventHandler(ctx.element_MouseLeave);
115 element.MouseMove += new System.Windows.Input.MouseEventHandler(ctx.element_MouseMove);
116 }
117 }
118 }
转载于:https://www.cnblogs.com/xiaotie/archive/2010/11/10/1874054.html
使用扩展方法对代码的行为进行封装的例子:封装UIElement的“拖动”相关推荐
- appsetting mysql_给IConfiguration写一个GetAppSetting扩展方法(示例代码)
给 IConfiguration 写一个 GetAppSetting 扩展方法 Intro 在 .net core 中,微软已经默认使用 appsettings.json 来代替 app.config ...
- 用 .NET 3.5 创建 ToJSON() 扩展方法
今年早些时候,我通过blog介绍了 C# 和 VB 语言的一项新的扩充特性"扩展方法". 扩展方法让开发者可以向已有的 CLR 类型的公共契约中添加新的方法,而不需要子类化或重新编 ...
- c#扩展方法奇思妙用性能篇一:扩展方法性能初测
最近写了几篇<c#扩展方法奇思妙用>的文章,一直只是讨论如何扩展.如何使用的问题,几乎没有涉及效率方面. 而大家的回复好多都在问效率如何.性能怎样,也引起了我对效率的关注,今天将初步测试的 ...
- .NET中那些所谓的新语法之二:匿名类、匿名方法与扩展方法
开篇:在上一篇中,我们了解了自动属性.隐式类型.自动初始化器等所谓的新语法,这一篇我们继续征程,看看匿名类.匿名方法以及常用的扩展方法.虽然,都是很常见的东西,但是未必我们都明白其中蕴含的奥妙.所以, ...
- 技巧/诀窍:用 .NET 3.5 创建 ToJSON() 扩展方法 (木野狐译)
[原文地址] Tip/Trick: Building a ToJSON() Extension Method using .NET 3.5 [原文发表日期] Monday, October 01, 2 ...
- 用 .NET 3.5 创建 ToJSON() 扩展方法 (木野狐译)
[原文地址] Tip/Trick: Building a ToJSON() Extension Method using .NET 3.5 [原文发表日期] Monday, October 01, ...
- Kotlin扩展方法进化之Context Receiver
/ 今日科技快讯 / 近日,福特汽车公司宣布将削减总计3000个受薪和合同工职位,被裁员目标主要位于北美和印度.此举表明,该公司正在进行重组,以在开发软件驱动的电动汽车领域追赶特斯拉. / ...
- MongoDB:利用官方驱动改装为EF代码风格的MongoDB.Repository框架 五 --- 为ListMongoDBRef增加扩展方法...
本次改动主要内容:为List<MongoDBRef>增加扩展方法 在MongoDB.Repository的使用过程中,发现在一个类中只定义一个List<MongoDBRef>是 ...
- 【Groovy】Groovy 扩展方法 ( 扩展静态方法示例 | 扩展实例方法示例 | 扩展实例方法与扩展静态方法代码相同 )
文章目录 一.扩展静态方法示例 二.扩展实例方法示例 三.扩展实例方法与扩展静态方法代码相同 一.扩展静态方法示例 在上一篇博客 [Groovy]Groovy 扩展方法 ( Groovy 扩展方法引入 ...
最新文章
- 用Groovy思考 第一章 用Groovy简化Java代码
- python必刷面试_Python面试必刷题系列(5)
- MVC命名空间中的~UrlHelper中的Action方法告诉我们方法重载的重要性(路由的统一)...
- VOC2007xml转YOLO的txt格式代码
- SpringMVC自学日志03(SpringMVC注解)
- Ubuntu开启或重启ssh服务
- OpenGL基础54:点光源阴影
- JS取得RadioButtonList的Value,Text及选中值等信息
- 2019开放大学计算机应用基础,国家开放大学2019年电大计算机应用基础考试试题一试卷(国家开放大学).doc...
- matlab imagesc 平滑,在matlab中,如何使用imagesc在2D热图中“平滑”像素
- 科学计算机恢复初始化,快速解决Windows 10系统还原一直初始化或卡住的方法!...
- Teamviewer远程,应用界面显示空白
- 微信公众号(订阅号)申请流程
- 怎么用计算机算分数加减法,怎么算分数加减法?怎么教给孩子?
- Vue3.0项目——打造企业级音乐App(二)图片懒加载、v-loading指令的开发和优化
- CRM学习笔记类转换工具(pojo互转)上下文中获取用户名cookie工具
- EtherCAT之Lan9252调试笔记
- 皮卡智能2022年功能升级汇总,打造智能工作新体验
- java线程中的tid_jstack中的tid到底是什么意思呢
- 半年亏损超5亿美元/股价腰斩,这家自动驾驶公司日子不好过