Quelle est la meilleure façon de structurer ce Linq-à-événements glisser et déposer le code?
-
24-09-2019 - |
Question
Je suis en train de gérer une interaction glisser-déposer, ce qui implique vers le bas la souris, déplacer la souris, et jusqu'à la souris.
Voici une repro simplifiée de ma solution:
- sur la souris vers le bas, crée une ellipse et l'ajoute à une toile
- sur le mouvement de la souris, repositionne l'ellipse pour suivre la souris
-
sur place de la souris, change la couleur de la toile de sorte qu'il est évident que l'on vous glisser.
var mouseDown = Observable.FromEvent<MouseButtonEventArgs>(canvas, "MouseLeftButtonDown"); var mouseUp = Observable.FromEvent<MouseButtonEventArgs>(canvas, "MouseLeftButtonUp"); var mouseMove = Observable.FromEvent<MouseEventArgs>(canvas, "MouseMove"); Ellipse ellipse = null; var q = from start in mouseDown.Do(x => { // handle mousedown by creating a red ellipse, // adding it to the canvas at the right position ellipse = new Ellipse() { Width = 10, Height = 10, Fill = Brushes.Red }; Point position = x.EventArgs.GetPosition(canvas); Canvas.SetLeft(ellipse, position.X); Canvas.SetTop(ellipse, position.Y); canvas.Children.Add(ellipse); }) from delta in mouseMove.Until(mouseUp.Do(x => { // handle mouse up by making the ellipse green ellipse.Fill = Brushes.Green; })) select delta; q.Subscribe(x => { // handle mouse move by repositioning ellipse Point position = x.EventArgs.GetPosition(canvas); Canvas.SetLeft(ellipse, position.X); Canvas.SetTop(ellipse, position.Y); });
le XAML est simplement
<Canvas x:Name="canvas"/>
Il y a quelques choses que je n'aime pas ce code, et je besoin d'aide refactorisation il:)
D'abord: le callbacks mousedown et mouseUp sont spécifiés comme des effets secondaires. Si deux abonnements sont faits à q
, ils se produiront deux fois.
En second lieu, le rappel mouseup est spécifié avant le rappel mousemove. Cela en fait un peu difficile à lire.
En troisième lieu, la référence à l'ellipse semble être dans un endroit stupide. S'il y a deux abonnements, cette référence variable assez rapidement être écrasé. Je suis sûr qu'il devrait y avoir une certaine façon nous pouvons tirer parti du mot-clé let
d'introduire une variable à l'expression LINQ qui signifie la référence ellipse correcte est disponible à la fois le déplacement de la souris et les gestionnaires de la souris sont
Comment voulez-vous écrire ce code?
La solution
Pour éviter les effets secondaires subscrition, vous devez publier votre observable. Je pense que quelque chose comme ça serait Ok:
public MainWindow()
{
InitializeComponent();
var mouseDown = Observable
.FromEvent<MouseButtonEventArgs>(this, "MouseLeftButtonDown");
var mouseUp = Observable
.FromEvent<MouseButtonEventArgs>(this, "MouseLeftButtonUp");
var mouseMove = Observable
.FromEvent<MouseEventArgs>(this, "MouseMove");
var ellipses = mouseDown
.Select(args => new {
a = args,
el = new Ellipse
{
Width = 10, Height = 10, Fill = Brushes.Red
}})
.Publish();
ellipses
.Subscribe(elargs =>
{
var position = elargs.a.EventArgs.GetPosition(canvas);
Canvas.SetLeft(elargs.el, position.X);
Canvas.SetTop(elargs.el, position.Y);
canvas.Children.Add(elargs.el);
});
var elmove = from elargs in ellipses
from mm in mouseMove.TakeUntil(mouseUp)
select new { a = mm, el = elargs.el };
elmove.
Subscribe(elargs =>
{
var position = elargs.a.EventArgs.GetPosition(canvas);
Canvas.SetLeft(elargs.el, position.X);
Canvas.SetTop(elargs.el, position.Y);
});
var elmup = from elargs in ellipses
from mup in mouseUp
select elargs.el;
elmup.Subscribe(el => el.Fill = Brushes.Green);
ellipses.Connect();
}