¿Cuál es la mejor manera de estructurar esta Arrastre LINQ-a-Eventos y código de gota?
-
24-09-2019 - |
Pregunta
Estoy tratando de manejar una interacción de arrastrar y soltar, lo que implica abajo del ratón, mover el ratón y el ratón hacia arriba.
Aquí está una simplificado repro de mi solución que:
- abajo del ratón, crea una elipse y lo añade a un lienzo
- al mover el ratón, reposiciona la elipse para seguir al ratón
-
en ratón hacia arriba, cambia el color de la tela de manera que es obvio que uno que está arrastrando.
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); });
el XAML es simplemente
<Canvas x:Name="canvas"/>
Hay algunas cosas que no me gusta de este código, y necesito ayuda refactorización:)
En primer lugar: la mousedown y devoluciones de llamada mouseup se especifican como efectos secundarios. Si dos suscripciones se hacen para q
, pasarán dos veces.
En segundo lugar, se especifica la devolución de llamada mouseup antes mousemove la devolución de llamada. Esto hace que sea un poco difícil de leer.
En tercer lugar, la referencia a la elipse parece estar en un lugar tonto. Si hay dos suscripciones, que la referencia variable se sobrescribirá con bastante rapidez. Estoy seguro de que debe haber alguna manera podemos aprovechar la palabra clave let
para introducir una variable a la expresión LINQ que significará la referencia correcta elipse está disponible tanto para el movimiento del ratón y ratón hacia arriba manipuladores
¿Cómo escribir el código?
Solución
Para evitar subscripción efectos secundarios, debe publicar su observable. Creo que algo como esto estaría bien:
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();
}