2010-12-03 44 views
2

J'apprends lentement comment utiliser Reactive Extensions for .NET avec WPF. Il y a quelques exemples de débutants sur la façon simple d'écrire des routines de drag-drop ou de dessin, mais ils sont tous extrêmement simples. J'essaie d'aller un peu plus loin et ce n'est pas évident pour moi de savoir quelle est la "bonne" façon.Quelle est la bonne façon de déterminer la fin d'un glisser de la souris en utilisant Rx?

Les exemples montrent tous comment vous pouvez définir des flux d'événements de MouseDown, MouseMove et MouseUp

var mouseDown = from evt in Observable.FromEvent<MouseButtonEventArgs>(..., "MouseDown") 
       select evt.EventArgs.GetPosition(...); 

var mouseMoves = from evt in Observable.FromEvent<MouseEventArgs>(..., "MouseMove") 
       select evt.EventArgs.GetPosition(...); 

var mouseUp = Observable.FromEvent<MouseButtonEventArgs>(..., "MouseUp"); 

Et comment vous pouvez facilement faire des choses au cours d'une MouseDrag (cela affiche les coordonnées du rectangle créé à partir du point de glisser à partir à la position actuelle de la souris)

var mouseDrag = from start in mouseDown 
       from currentPosition in mouseMoves.TakeUntil(mouseUp) 
       select new Rect(Math.Min(start.X, currentPosition.X), 
           Math.Min(start.Y, currentPosition.Y), 
           Math.Abs(start.X - currentPosition.X), 
           Math.Abs(start.Y - currentPosition.Y)); 

mouseDrag.Subscribe(x => 
      { 
       Info.Text = x.ToString(); 
      }); 

Ma question est, quelle est la manière « appropriée » pour accomplir une tâche à la fin de la traînée de la souris? A l'origine, je pensais que je pouvais faire quelque chose comme ceci:

mouseDrag.Subscribe(
    onNext: x => 
      { 
       Info.Text = x.ToString(); 
      }, 
    onCompleted:() => 
       { 
       // Do stuff here...except it never gets called 
       }); 

Lire plus de la documentation, cependant, il semble que onCompleted est appelé quand il n'y a plus de données (jamais) et lorsque l'objet peut être mis au rebut.

Donc la première option qui semble plausible est de s'abonner à l'événement mouseUp et d'y faire quelque chose.

mouseUp.Subscribe(x => 
      { 
       // Do stuff here.. 
      } 

Mais à ce moment, je peux aussi bien revenir à simplement utiliser le gestionnaire d'événements MouseLeftButtonUp « normal ».

Existe-t-il une autre façon de déterminer quand le mouseDrag est "terminé" (ou quand le TakeUntil(mouseUp)) se produit et d'effectuer une action alors?

Répondre

5

La séquence ne se termine jamais car la source (MouseDown) ne se termine jamais (il s'agit d'un événement). Il est à noter qu'un IObservable ne peut pas appeler OnComplete d'un abonné plus d'une fois, cela fait partie du contrat (OnNext* (OnCompleted|OnError)?).

Pour savoir quand la séquence mouseMove.TakeUntil(mouseUp) complète, vous devez connecter à l'appel à SelectMany:

public static IDisposable TrackDrag(this UIElement element, 
    Action<Rect> dragging, Action dragComplete) 
{ 
    var mouseDown = Observable.FromEvent(...); 
    var mouseMove = Observable.FromEvent(...); 
    var mouseUp = Observable.FromEvent(...); 

    return (from start in mouseDown 
      from currentPosition in mouseMove.TakeUntil(mouseUp) 
        .Do(_ => {},() => dragComplete()) 
      select new Rect(Math.Min(start.X, currentPosition.X), 
          Math.Min(start.Y, currentPosition.Y), 
          Math.Abs(start.X - currentPosition.X), 
          Math.Abs(start.Y - currentPosition.Y)); 
      ).Subscribe(dragging); 
} 

Ensuite, vous pouvez l'utiliser comme ceci:

element.TrackDrag(
    rect => { }, 
    () => {} 
); 

Pour l'intérêt de clarté, voici l'expression LINQ utilisant les méthodes d'extension sous-jacentes:

return mouseDown.SelectMany(start => 
{ 
    return mouseMove 
     .TakeUntil(mouseUp) 
     .Do(_ => {},() => dragComplete()) 
     .Select(currentPosition => new Rect(...)); 
}) 
.Subscribe(dragging); 

En d'autres termes, pour chaque valeur de mouseDown, une nouvelle séquence sera souscrite. Lorsque cette séquence se termine, appelez dragComplete().

+0

Oui - Je me suis rendu compte après mon premier essai n'a pas fonctionné que la façon OnCompleted n'est pas la voie à suivre. Merci pour l'explication plus complète et en incluant Do() :) – Jedidja