表的内容 系统要求反应面一个简单的计时器从事件中收集数据序列使用更复杂的查询订阅您希望完成的面最终考虑历史 介绍 “Rx”代表反应性扩展,它是微软DevLabs的项目计划之一。DevLabs是微软团队开发具有代表性技术的地方。这样的原型项目被发布,然后由开发社区进行评估,根据它们的成功,有一天它们可能会成为。net框架的一部分,或者成为一个新的工具,等等。 从. net框架的第一个版本开始,甚至更早之前,开发人员就一直在处理各种各样的事件:UI事件(如按键和鼠标移动)、时间事件(如计时器滴答)、异步事件(如Web服务响应异步调用),等等。反应性扩展诞生于DevLabs团队设想这些事件类型之间的“共性”时。他们努力为我们提供以更聪明的方式处理不同事件的工具。本文展示了一些可用于响应式扩展的实用技术,希望它们对您将来的项目有用。 系统需求 要使用本文提供的WPF ReactiveFace,如果您已经拥有Visual Studio 2010,那么这就足以运行应用程序了。如果你没有,你可以下载以下100%免费的开发工具直接从微软: 此外,你必须下载和安装Rx for .NET Framework 4.0通过点击按钮相同的名字从DevLabs页面: Visual c# 2010 express devlabs: .NET (Rx)响应式扩展 反应面项目 反应面是一个利用反应扩展的小WPF项目。你在屏幕上看到的这个丑陋的、令人毛骨悚然的、不完整的小脑袋只是用来说明一些Rx特性的借口。 当你运行这个项目时,你会注意到的第一件事是眼皮在眨(即使没有眼球!)正如我之前所说的,这个小应用程序是用来演示Rx的特性的,所以让我们从blink开始。 眨眼本身是由一个移动“皮肤”的动画完成的,也就是覆盖在脸部眼洞后面的矩形。一旦动画开始,矩形快速地上下移动,模拟一个眨眼。 动画存储在故事板中,故事板又存储在窗口XAML中: 隐藏,收缩,复制Code

<Window.Resources>
...
<Storyboardx:Key="sbBlinkLeftEye">
<DoubleAnimationx:Name="daBlinkLeftEye"Storyboard.TargetName="recEyelidLeft"Storyboard.TargetProperty="Height"From="18"To="48"Duration="0:0:0.100"AutoReverse="True">
</DoubleAnimation>
</Storyboard>
<Storyboardx:Key="sbBlinkRightEye">
<DoubleAnimationx:Name="daBlinkRightEye"Storyboard.TargetName="recEyelidRight"Storyboard.TargetProperty="Height"From="18"To="48"Duration="0:0:0.100"AutoReverse="True">
</DoubleAnimation>
</Storyboard>
</Window.Resources>

一个简单的定时器 下面的代码将启动故事板,这样闪烁将每2000毫秒(2秒)发生一次。如果您对动画有一点了解,我相信您现在一定在问自己:“为什么不在XAML本身中设置一个重复行为为永远?”好吧,虽然您是对的,但我必须说这只是为了说明如何使用Rx代码来实现这一点。 隐藏,复制Code

//Find and store storyboard resources
var sbBlinkLeftEye = (Storyboard)FindResource("sbBlinkLeftEye");
var sbBlinkRightEye = (Storyboard)FindResource("sbBlinkRightEye"); //Set a new observable sequence which produces
//a value each 2000 milliseconds
var blinkTimer = Observable.ObserveOnDispatcher(
Observable.Interval(TimeSpan.FromMilliseconds(2000))
); //Subscribe to the timer sequence, in order
//to begin both blinking eye storyboards
blinkTimer.Subscribe(e =>
{
sbBlinkLeftEye.Begin();
sbBlinkRightEye.Begin();
}
);

上面代码片段中的第一行很简单:他们从XAML中找到并实例化故事板变量。接下来,我们有可观察的。ObserveOnDispatcher方法,稍后我会解释。然后是重要的部分:可观察的. interval (timespane . frommilliseconds(2000))。这段代码返回一个可观察序列,该序列在每个周期之后(在本例中为每2秒)生成一个值。如果你认为“这是个计时器”,那你就完全正确了。它是一个计时器,我们用它作为计时器。请注意,这已经是Rx框架提供的一个新特性。因此,虽然您可以使用DispatcherTimer或其他内置的。net计时器,但您现在有了新的可观察对象。间隔法执行相同的任务。但是,使用可观察序列的好处是,您将在后面看到,您可以使用LINQ来操作序列的生成方式。 上面代码示例中的最后一行告诉应用程序,每当可观察序列产生一个值时,就启动闪烁的故事板。也就是说,每2秒钟,我们丑陋的脸就会眨一次眼。记住可观察的。上面ObserveOnDispatcher行吗?使用这个方法是为了使我们在访问故事板对象时不会得到错误的线程异常(它是在与计时器线程不同的线程中创建的)。 收集数据 连同MainWindow.xaml。cs,你会看到一个私有类ElementAndPoint,你可能想知道它为什么在那里。它只是一个POCO(普通的CLR对象),它将帮助我们在移动鼠标和按/释放鼠标按钮时存储关于控件和点的信息。在下一节中,您将更清楚地看到这一点。 隐藏,复制Code

/// <summary/>
/// We use this private class just to gather data about the control and the point
/// affected by mouse events
/// </summary/>
private class ElementAndPoint
{
public ElementAndPoint(FrameworkElement element, Point point)
{
this.Element = element;
this.Point = point;
} public FrameworkElement Element { get; set; }
public Point Point { get; set; }
}

从事件序列 现在我们面临一种新的Rx方法:Observable.FromEvent。该方法返回一个可观察序列,其中包含底层。net事件的值。也就是说,我们告诉应用程序从MouseMove和MouseUp事件创建可观察序列,值和序列是由GetPosition函数: 隐藏,复制Code

//Create 2 observable sequences from mouse events
//targeting the MainWindow
var mouseMove = Observable.FromEvent<mouseeventargs />(this, "MouseMove").Select(
e => new ElementAndPoint(null, e.EventArgs.GetPosition(mainCanvas)));
var mouseUp = Observable.FromEvent<mousebuttoneventargs />(this, "MouseUp").Select(
e => new ElementAndPoint(null, e.EventArgs.GetPosition(mainCanvas)));

让我们仔细看看这几行: 这个可观察的部分告诉应用程序从MouseMove事件中创建一个可观察的MouseEventArgs序列,以当前窗口(this)作为目标元素。该指令本身将返回一个MouseEventArgs值序列,但在本例中,我们将修改序列值类型,通过使用Select方法为序列中的每个值返回一个新的ElementAndPoint对象。基本上,我们说元素是空的(也就是说,我们不关心元素),点是鼠标移动时相对于mainCanvas元素的位置。fromevent&mouseup使用了相同的逻辑,但是在本例中,我们必须小心地将源类型定义为MouseButtonEventArgs,这是MouseUp事件返回的类型。 接下来的两行还定义了两个不同事件的可观察序列:MouseEnter和MouseLeave。每当您在网格界面区域(由grdFace元素分隔)中输入鼠标时,第一个序列将生成单个值。当你离开这个区域时,第二个序列产生一个值。同样,我将在后面解释如何使用这些序列。 隐藏,复制Code

//Create 2 observable sequences from mouse events
//targeting the face grid
var mouseEnterFace = Observable.FromEvent<mouseeventargs />(grdFace, "MouseEnter").Select(
e => new ElementAndPoint(null, e.EventArgs.GetPosition(mainCanvas)));
var mouseLeaveFace = Observable.FromEvent<mouseeventargs />(grdFace, "MouseLeave").Select(
e => new ElementAndPoint(null, e.EventArgs.GetPosition(mainCanvas)));

使用更复杂的查询 然后是我们创建一个用户控件列表来定义面部部分(眼睛,眉毛,鼻子,嘴巴): 隐藏,复制Code

//We store a list of user controls (representing portions of the face)
//so that we can create new observable events and
//subscribe to them independently
var controlList = new List<usercontrol />();
controlList.Add(ucLeftEyeBrow);
controlList.Add(ucLeftEye);
controlList.Add(ucRightEyeBrow);
controlList.Add(ucRightEye);
controlList.Add(ucNose);
controlList.Add(ucMouth);

一旦我们有了这个列表,我们可以很容易地迭代它们的元素来创建可观察序列的事件,这些面向部分: 隐藏,复制Code

foreach (var uc in controlList)
{
//Initialize each user control with
//predefined Canvas attached properties.
Canvas.SetZIndex(uc, 1);
Canvas.SetLeft(uc, 0);
Canvas.SetTop(uc, 0);
. . .

现在我们在用户控件列表上迭代,我们根据MouseDown和MouseUp UI事件创建可观察序列。还要注意,我们使用Select方法返回一个ElementAndPoint对象序列,其中包含(FrameworkElement)e。发送方值作为元素。换句话说,序列中的每个值现在有: 鼠标按钮被按下或释放的位置,鼠标向下/向上事件发生的位置 隐藏,复制Code

//Create 2 observable sequence from mouse events
//targetting the current user control
var mouseDownControl = Observable.FromEvent<mousebuttoneventargs />(uc, "MouseDown").Select(
e => new ElementAndPoint((FrameworkElement)e.Sender, e.EventArgs.GetPosition(mainCanvas)));
var mouseUpControl = Observable.FromEvent<mousebuttoneventargs />(uc, "MouseUp").Select(
e => new ElementAndPoint((FrameworkElement)e.Sender, e.EventArgs.GetPosition(mainCanvas)));

刚开始的时候,它的语法可能看起来有点奇怪,但是我相信,如果你用这个小例子来练习,你会习惯它的。 应用程序中的另一个重要部分是拖放功能。每个面部分都可以被拖放,这基本上是由两段代码完成的:第一部分是LINQ查询,它创建了一个可观察序列,当面部分被拖放时,该序列被填充。而第二段代码订阅该可观察序列并相应地移动面部部分: 隐藏,复制Code

//Create a observable sequence that starts producing values
//when the mouse button is down over a user control, and stops producing values
//once the mouse button is up over that control,
//while gathering information about mouse movements in the process.
var osDragging = from mDown in mouseDownControl
from mMove in mouseMove.StartWith(mDown).TakeUntil(mouseUp)
.Let(mm => mm.Zip(mm.Skip(1), (prev, cur) =>
new
{
element = mDown.Element,
point = cur.Point,
deltaX = cur.Point.X - prev.Point.X,
deltaY = cur.Point.Y - prev.Point.Y
}
))
select mMove; //Subscribe to the dragging sequence, using the information to
//move the user control around the canvas.
osDragging.Subscribe(e =>
{
Canvas.SetLeft(e.element, Canvas.GetLeft(e.element) + e.deltaX);
Canvas.SetTop(e.element, Canvas.GetTop(e.element) + e.deltaY);
}
);

上面的代码片段可以用浅显的英语翻译成:“当用户按下鼠标按钮了一些元素,而用户没有发布按钮,当用户移动鼠标在当前窗口,返回的序列值包含元素被拖动,鼠标指针位于,和代表坐标增量运动自最后一次鼠标移动。对于每个返回的值,根据计算得到的X, Y增量移动受影响元素的X, Y坐标。很容易,不是吗? 现在让我们仔细看看我们刚刚做了什么: 上述LINQ查询的核心是mouseMove可观察序列(我们在前面声明过)。StartWith和TakeUntil方法告诉应用程序观察序列必须开始/停止产生值的时间。zip (mm.Skip(1), (prev, cur)部分是一条将两个序列值合并为单个序列值的指令:这非常方便,因为它使我们能够使用前一个序列值和当前序列值,并将它们组合起来计算增量。匿名类型以new {element…修改返回的类型,以便我们可以获得关于拖放操作的更多信息。Subscribe方法描述了每次拖动面部部分时执行的操作。在我们的例子中,设置了该元素的左侧和顶部属性,因此可以移动该元素。 如你所愿订阅 继续下一部分:假设我们想让选中的部分移动到屏幕上其他元素的上方:在本例中,我们可以将ZIndex设置为一个高度值,比如100。然后我们要做的就是订阅另一个操作到mouseDownControl的观察序列,并修改元素的属性: 隐藏,复制Code

...

var mouseDownControl = Observable.FromEvent<mousebuttoneventargs />(uc, "MouseDown").Select(
e => new ElementAndPoint((FrameworkElement)e.Sender,
e.EventArgs.GetPosition(mainCanvas))); ... //Once the mouse button is up, the ZIndex is set to 100, that is,
//we want to make the user control to move on top of any other controls
//on the screen.
mouseDownControl.Subscribe(e =>
{
Canvas.SetZIndex(e.Element, 100);
}
);

使用相同的技术,我们可以在用户释放元素时将其放入正确的ZIndex值。在我们的例子中,这使得眼球停留在眼睑后面。我们通过订阅mouseUpControl序列来做到这一点: 隐藏,复制Code

    ...

    var mouseUpControl = Observable.FromEvent<mousebuttoneventargs />(uc, "MouseUp").Select(
e => new ElementAndPoint((FrameworkElement)e.Sender,
e.EventArgs.GetPosition(mainCanvas))); ... //Once the mouse button is down, the ZIndex is set to the proper value (1),
//unless for the eye controls, which are set to -1 in order to put them
//behind the face.
mouseUpControl.Subscribe(e =>
{
switch (e.Element.Name)
{
case "ucLeftEye":
case "ucRightEye":
Canvas.SetZIndex(e.Element, -1);
break;
default:
Canvas.SetZIndex(e.Element, 1);
break;
}
}
);
}

完成的脸 最后,我们订阅了mouseMove observable序列。注意这里有很多东西在动:眉毛在动,眼睛在看着鼠标光标,牙齿在上下移动。我们美丽丑陋的脸已经完成,注意我们的鼠标移动。 当然,我们可以使用单独的动作,甚至单独的函数。用它更好地服务于你。 隐藏,收缩,复制Code

var leftPupilCenter = new Point(60, 110);
var rightPupilCenter = new Point(130, 110); //Subscribe to the mousemove event on the MainWindow. This is used
//to move eyes and eyebrows.
mouseMove.Subscribe(e =>
{
double leftDeltaX = e.Point.X - leftPupilCenter.X;
double leftDeltaY = e.Point.Y - leftPupilCenter.Y;
var leftH = Math.Sqrt(Math.Pow(leftDeltaY, 2.0) + Math.Pow(leftDeltaX, 2.0));
var leftSin = leftDeltaY / leftH;
var leftCos = leftDeltaX / leftH; double rightDeltaX = e.Point.X - rightPupilCenter.X;
double rightDeltaY = e.Point.Y - rightPupilCenter.Y;
var rightH = Math.Sqrt(Math.Pow(rightDeltaY, 2.0) + Math.Pow(rightDeltaX, 2.0));
var rightSin = rightDeltaY / rightH;
var rightCos = rightDeltaX / rightH; if (!double.IsNaN(leftCos) &&
!double.IsNaN(leftSin))
{
ucLeftEye.grdLeftPupil.Margin =
new Thickness(leftCos * 16.0, leftSin * 16.0, 0, 0);
} if (!double.IsNaN(rightCos) &&
!double.IsNaN(rightSin))
{
ucRightEye.grdRightPupil.Margin =
new Thickness(rightCos * 16.0, rightSin * 16.0, 0, 0);
} var distFromFaceCenter = Math.Sqrt(Math.Pow(e.Point.X - 90.0, 2.0) +
Math.Pow(e.Point.Y - 169.0, 2.0)); ucLeftEyeBrow.rotateLeftEyebrow.Angle = -10 + 10 * (distFromFaceCenter / 90.0);
ucRightEyeBrow.rotateRightEyebrow.Angle = 10 - 10 * (distFromFaceCenter / 90.0); ucMouth.pnlTeeth.Margin =
new Thickness(0, 10 * (distFromFaceCenter / 90.0) % 15, 0, 0);
}
);

最后考虑 正如我之前所说的,这只是对Rx功能的一瞥。当然,响应式扩展还可以做更多的工作,但是如果本文在某种程度上对您有用,我将非常高兴。要了解更多关于Rx的方法,请在代码项目中阅读其他优秀的Rx文章: 有趣的Rx探索反应扩展(Rx)通过Twitter和Bing地图通过示例mashup的Rx框架 历史 2011-02-27:初始版本。 本文转载于:http://www.diyabc.com/frontweb/news29898.html

使用响应扩展的响应面(Rx)的更多相关文章

  1. flask框架(九): 请求和响应扩展以及中间件

    一:请求响应扩展 # 每一次访问都执行 # 注意请求之前按照顺序执行 # 请求之后按照书写顺序倒序执行 # 请求之前执行 @app.before_request def process_request ...

  2. 《MFC游戏开发》笔记四 键盘响应和鼠标响应:让人物动起来

    本系列文章由七十一雾央编写,转载请注明出处. http://blog.csdn.net/u011371356/article/details/9327377 作者:七十一雾央 新浪微博:http:// ...

  3. HttpServletResponse ServletResponse 返回响应 设置响应头设置响应正文体 重定向 常用方法 如何重定向 响应编码 响应乱码

    HttpServletResponse  和 ServletResponse  都是接口 具体的类型对象是由Servlet容器传递过来   ServletResponse对象的功能分为以下四种:   ...

  4. Jmeter(十二)响应断言之响应文本和响应信息的差别

    在Jmeter的后置处理器中添加响应断言, 要测试的响应字段中有两个很难区分的选项, 响应文本和响应信息. 我做了两个小实验来进行区别. 1. 用Fiddler捕捉了一个POST请求, 其响应是suc ...

  5. HttpServletResponse ServletResponse 返回响应 设置响应头设置响应正文体 重定向 常用方法 如何重定向 响应编码 响应乱码

    原文地址:HttpServletResponse ServletResponse 返回响应 设置响应头设置响应正文体 重定向 常用方法 如何重定向 响应编码 响应乱码 HttpServletRespo ...

  6. JAVAEE 和项目开发(第四课:HTTP的响应格式和响应状态码)

    HTTP 协议之响应 响应格式的结构: 响应行(状态行):HTTP 版本.状态码.状态消息 响应头:消息报头,客户端使用的附加信息 空行:响应头和响应实体之间的,必须的. 响应实体:正文,服务器返回给 ...

  7. dubbo协议之响应头编码器&响应对象编码

    前2节分析完了请求头和请求对象的编码,这里看一下响应头和响应对象的编码: 和请求头部一样进来先指定序列化器,没有的话用默认的Hessian2,接下来2个字节的操作和请求头编码类似,第三个字节时去req ...

  8. 响应式编程知多少 | Rx.NET 了解下

    1. 引言 An API for asynchronous programming with observable streams. ReactiveX is a combination of the ...

  9. HTTP请求响应机制与响应状态码

    转载来源:http://blog.csdn.net/xyw591238/article/details/51907143 HTTP协议 Internate的基本协议是TCP/IP(传输控制协议和网际协 ...

随机推荐

  1. Infinite Inversions(树状数组+离散化)

    思路及代码参考:https://blog.csdn.net/u014800748/article/details/45420085 There is an infinite sequence cons ...

  2. 在一台电脑上,添加多个Git的ssh key

    Git的第一套公秘钥默认名为 id_rsa ,如果你想要生成另外一个公钥,比如 aysee ,你也可以使用任何你喜欢的名字. 步骤如下:(总共四大操作) 一.生成ssh key 1.生成一个新的自定义 ...

  3. 剑指 Offer 55 - II. 平衡二叉树

    题目描述 输入一棵二叉树的根节点,判断该树是不是平衡二叉树.如果某二叉树中任意节点的左右子树的深度相差不超过1,那么它就是一棵平衡二叉树. 示例1: 给定二叉树 [3,9,20,null,null,1 ...

  4. 微信小程序(1)

    微信小程序 什么是微信小程序? 微信小程序优点 跨平台 打开速度比h5快 不用下载 开发目录结构介绍 1. 小程序Pages目录说明 2. 工具文件夹 3. 全局文件 用法1 全局APP.json文件 ...

  5. 转:brpc的研发经历

    转载自:https://www.jianshu.com/p/124dc2c7d9d3 RPC是个老概念,五花八门的实现非常多.在14年我刚转到基础架构部时,其实是不想做RPC框架的.我的想法可能和很多 ...

  6. ssh工具 (Java)

    执行shell命令.下载文件... package com.sunsheen.blockchain.admin.utils; import java.io.BufferedReader; import ...

  7. elasticsearch跨集群数据迁移

    写这篇文章,主要是目前公司要把ES从2.4.1升级到最新版本7.8,不过现在是7.9了,官方的文档:https://www.elastic.co/guide/en/elasticsearch/refe ...

  8. Jenkins+Git+Gitlab+Ansible实现持续集成自动化部署静态网站(6)

    前言 在之前已经写了关于Git,Gitlab以及Ansible的两篇博客<Git+Gitlab+Ansible剧本实现一键部署Nginx–技术流ken>,<Git+Gitlab+An ...

  9. Azure Cosmos DB介绍及演示

    Azure Cosmos DB 是 Microsoft 提供的全球分布式多模型数据库服务.Cosmos DB是一种NoSql数据库,但是它兼容多种API.它支持SQL, MongoDB.Cassand ...

  10. 秋招进大厂其实也就那么回事,你会这样卡进大厂的BUG吗?

    在BAT这种大厂里,只要肯吃苦,技术和工资进步的速度会超出你想象,我在上海,按当前价格算,一般在大厂里干个三四年,好歹房子的首付应该能有,而且这种房子还不是太偏远太小的. 进大厂确实需要一定的实力,但 ...