2013-07-09 74 views
3

我是一个Rx新手,想要弄清楚如何用Rx处理鼠标手势。我发现这个解决方案在某处:使用Rx进行鼠标输入 - 制作一系列可观察对象?

var mouseMove = Observable.FromEventPattern<MouseEventArgs>(this, "MouseMove"); 
var lMouseDown = Observable.FromEventPattern<MouseEventArgs>(this, "MouseDown") 
    .Where(e => e.EventArgs.Button == MouseButtons.Left); 
var lMouseUp = Observable.FromEventPattern<MouseEventArgs>(this, "MouseUp") 
    .Where(e => e.EventArgs.Button == MouseButtons.Left); 
var dragSequence = 
    from down in lMouseDown 
    from move in mouseMove.StartWith(down).TakeUntil(lMouseUp) 
    select move; 
dragSequence.ObserveOn(this).Subscribe(e => Trace.WriteLine(e.EventArgs.Location)); 

但是多个独立的鼠标手势都是同一个流的一部分。所以我不能使用onCompleted的处理程序;该序列从未完成。我希望将流分成每个拖动的单独序列,我该怎么做?

回答

0

这里有一种方法:

var dragSequences = dragSequence.TakeUntil(lMouseUp) 
    .Concat(Observable.Return<MouseEventArgs>(null)) // send a NULL event after a drag completes 
    .Repeat(); // then start listening for the next drag gesture 

dragSequences.ObserveOn(this).Subscribe(e => 
{ 
    if (e == null) 
    { 
     // the previous drag operation has completed. Take any actions you need 
    } 
    else 
    { 
     // drag event. If the event is for MouseDown then it is the start of a new drag 
    } 
}); 
+0

的'dragSequence'的问题从来没有完成,所以我不认为这是有道理的'Concat'什么结束它。虽然'Repeat()'方法给了我一个想法,但... – Qwertie

+1

啊,你是对的。我添加了一个对'TakeUntil(lMouseUp)'的调用,这会在拖动操作完成时导致序列结束。您可以从您的dragSequence定义中删除'.TakeUntil'调用,因为它不再需要。 – Brandon

3

这里是我的解决方案:

var mouseMove = Observable.FromEventPattern<MouseEventArgs>(this, "MouseMove"); 
var lMouseDown = Observable.FromEventPattern<MouseEventArgs>(this, "MouseDown") 
    .Where(e => e.EventArgs.Button == MouseButtons.Left); 
var lMouseUp = Observable.FromEventPattern<MouseEventArgs>(this, "MouseUp") 
    .Where(e => e.EventArgs.Button == MouseButtons.Left); 

lMouseDown.SelectMany(start => 
{ 
    // a new drag event has started, prepare to receive input 
    var dragSeq = new List<Point>(); 
    Action<EventPattern<MouseEventArgs>, bool> onNext = (e, mouseUp) => { 
     // This code runs for each mouse move while mouse is down. 
     // In my case I want to constantly re-analyze the shape being 
     // drawn, so I make a list of points and send it to a method. 
     dragSeq.Add(e.EventArgs.Location); 
     AnalyzeGesture(dragSeq, mouseUp); 
    }; 

    return mouseMove 
     .StartWith(start) 
     .TakeUntil(lMouseUp.Do(e => onNext(e, true))) 
     .Do(e => onNext(e, false)); 
}) 
.Subscribe(); 

这是如何工作的,每个鼠标按下到达时,start=>{...}拉姆达运行。这个lambda返回一个使用Do()来处理每个输入的可观察元素。请注意,lambda本身创建一个事件流而不订阅它,并且我放弃了内部和外部可观察的结果,因为Do()已经处理了输入。

由于外部Subscribe()具有订阅单个鼠标拖动及其全部顺序(感谢SelectMany)的效果,因此lambda不订阅查询。

如果鼠标点与上次鼠标移动不同,我使用Do()来捕获它。但是,看起来鼠标点始终等于前一点。所以这里有一个忽略了鼠标释放点稍微简单的版本:

lMouseDown.SelectMany(start => 
{ 
    var dragSeq = new List<Point>(); 
    return mouseMove 
     .StartWith(start) 
     .TakeUntil(lMouseUp) 
     .Do(e => { 
      dragSeq.Add(e.EventArgs.Location); 
      AnalyzeGesture(dragSeq, false); 
     },() => AnalyzeGesture(dragSeq, true)); 
}) 
.Subscribe(); 
+0

这看起来像是'.Scan'运算符应该可以做到的事情:http://www.introtorx.com/content/v1.0.10621.0/07_Aggregation.html#Scan – AlexFoxGill

1

要在这里扩大我的意见是,你可能会使用.Scan操作

Func<List<T>, T, List<T>> AddWithNew = (list, t) => 
{ 
    var newList = list.ToList(); 
    newList.Add(t); 
    return newList; 
} 

var dragGestures = from start in lMouseDown 
        select mouseMove.StartWith(start) 
         .TakeUntil(lMouseUp) 
         .Scan(new List<Point>(), AddWithNew); 

dragGestures.Subscribe(listOfPoints => Console.WriteLine(listOfPoints)); 

序列仍然有办法“永无止境”,但您会收到增长点的名单,你Subscribe方法的新行开始时被重置为1:

[(0,0]    // Mouse down 
[(0,0), (1,1)]  // Mouse move 
[(0,0), (1,1), (1,0)] // Mouse up 
[(6,7)]    // Mouse down again 

您合作ULD也使用.Window运营商的序列划分成序列的序列:

var dragSequences = from start in lMouseDown 
        select mouseMove.StartWith(start) 
         .TakeUntil(lMouseUp) 
         .Scan(new List<Point>(), AddWithNew) 
         .Window(() => lMouseUp); 

dragSequences.Subscribe(seq => 
{ 
    seq.Subscribe(list => Analyze(list, false); 
    seq.Last().Subscribe(list => Analyze(list, true); 
}); 
相关问题