Domanda

Sto cercando di gestire un'interazione drag & drop, che coinvolge giù il mouse, spostare il mouse e mouse verso l'alto.

Ecco un semplificato Repro della mia soluzione che:

  • giù del mouse, crea un'ellisse e lo aggiunge una tela
  • il movimento del mouse, riposiziona l'ellisse di seguire il mouse
  • il mouse verso l'alto, cambia il colore della tela in modo che sia ovvio che quello che si sta trascinando.

    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);
    });
    

il codice XAML è semplicemente

    <Canvas x:Name="canvas"/>

Ci sono alcune cose che non mi piace di questo codice, e ho bisogno di aiuto refactoring esso:)

Prima di tutto: il MouseDown e MouseUp callback sono specificati come effetti collaterali. Se due abbonamenti sono fatti per q, accadranno due volte.

In secondo luogo, la richiamata MouseUp viene specificato prima la richiamata MouseMove. Questo lo rende un po 'difficile da leggere.

In terzo luogo, il riferimento al dell'ellisse sembra di essere in un posto stupido. Se ci sono due abbonamenti, che il riferimento variabile sarà sovrascritto abbastanza rapidamente. Sono sicuro che ci dovrebbe essere un modo siamo in grado di sfruttare la parola let di introdurre una variabile per l'espressione LINQ che significherà il riferimento dell'ellisse corretto è a disposizione sia il movimento del mouse e il mouse fino gestori

Come si scrive questo codice?

È stato utile?

Soluzione

Per evitare di abbonamento x effetti collaterali, si dovrebbe pubblicare il osservabile. Penso che qualcosa di simile non sarebbe male:

        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();
    }
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top