2013-07-09 52 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); 
}); 
相關問題