web-dev-qa-db-fra.com

C # comment faire une boucle en maintenant le bouton de la souris enfoncé

Pouvez-vous me diriger dans la bonne direction? J'essaie de déclencher une boucle lorsque le bouton de formulaire est enfoncé.

//pseudocode
While (button1 is pressed)
value1 += 1

Et puis bien sûr arrêter de boucler quand le bouton est relâché

13
Sinaesthetic

Pour éviter l'utilisation de threads, vous pouvez ajouter un composant Timer sur votre fiche/contrôle et l'activer simplement avec la souris enfoncée et le désactiver avec la souris levée. Ensuite, insérez le code que vous auriez normalement placé dans la boucle dans l'événement Timer_Tick. Si vous souhaitez utiliser System.Timers.Timer, vous pouvez utiliser plutôt l'événement Timer.Elapsed.

Exemple (en utilisant System.Timers.Timer):

using Timer = System.Timers.Timer;
using Systems.Timers;
using System.Windows.Forms;//WinForms example
private static Timer loopTimer;
private Button formButton;
public YourForm()
{ 
    //loop timer
    loopTimer = new Timer();
    loopTimer.Interval = 500;/interval in milliseconds
    loopTimer.Enabled = false;
    loopTimer.Elapsed += loopTimerEvent;
    loopTimer.AutoReset = true;
    //form button
    formButton.MouseDown += mouseDownEvent;
    formButton.MouseUp += mouseUpEvent;
}
private static void loopTimerEvent(Object source, ElapsedEventArgs e)
{
    //do whatever you want to happen while clicking on the button
}
private static void mouseDownEvent(object sender, MouseEventArgs e)
{
    loopTimer.Enabled = true;
}
private static void mouseUpEvent(object sender, MouseEventArgs e)
{
    loopTimer.Enabled = false;
}       
24
Doggett

Vous pouvez utiliser un fil pour compter et arrêter le fil lorsque la souris est relâchée. Ce qui suit a bien fonctionné pour moi:

var b = new Button { Text = "Press me" };

int counter = 0;
Thread countThread = null;
bool stop = false;

b.MouseDown += (s, e) =>
{
    stop = false;
    counter = 0;
    countThread = new Thread(() =>
    {
        while (!stop)
        {
            counter++;
            Thread.Sleep(100);
        }
    });
    countThread.Start();
};

b.MouseUp += (s, e) =>
{
    stop = true;
    countThread.Join();
    MessageBox.Show(counter.ToString());
};

Bien sûr, si vous souhaitez que les gestionnaires d'événements soient des méthodes plutôt que des lambdas, vous devrez transformer toutes les variables en champs.

11
Timwi
    private void button1_MouseDown(object sender, MouseEventArgs e)
    {
        timer1.Enabled = true;
        timer1.Start();

    }

    private void button1_MouseUp(object sender, MouseEventArgs e)
    {
        timer1.Stop();
    }



    private void timer1_Tick(object sender, EventArgs e)
    {
        numericUpDown1.Value++;

    }
7
fahdovski

Ce que j'ai lu ici m'a inspiré et j'ai décidé d'écrire ma propre classe de boutons appelée RepeatingButton. Au premier clic, il attend 500 ms, puis répète chaque fois 300 ms jusqu'à 2 s, puis se répète toutes les 100 ms (c.-à-d. Qu'il utilise l'accélération).

Voici le code;

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;

/// <summary>
/// A repeating button class.
/// When the mouse is held down on the button it will first wait for FirstDelay milliseconds,
/// then press the button every LoSpeedWait milliseconds until LoHiChangeTime milliseconds,
/// then press the button every HiSpeedWait milliseconds
/// </summary>
public class RepeatingButton : Button
{
    /// <summary>
    /// Initializes a new instance of the <see cref="RepeatingButton"/> class.
    /// </summary>
    public RepeatingButton()
    {
        internalTimer = new Timer();
        internalTimer.Interval = FirstDelay;
        internalTimer.Tick += new EventHandler(internalTimer_Tick);
        this.MouseDown += new MouseEventHandler(RepeatingButton_MouseDown);
        this.MouseUp += new MouseEventHandler(RepeatingButton_MouseUp);
    }

    /// <summary>
    /// The delay before first repeat in milliseconds
    /// </summary>
    public int FirstDelay = 500;

    /// <summary>
    /// The delay in milliseconds between repeats before LoHiChangeTime
    /// </summary>
    public int LoSpeedWait = 300;

    /// <summary>
    /// The delay in milliseconds between repeats after LoHiChangeTime
    /// </summary>
    public int HiSpeedWait = 100;

    /// <summary>
    /// The changeover time between slow repeats and fast repeats in milliseconds
    /// </summary>
    public int LoHiChangeTime = 2000;

    private void RepeatingButton_MouseDown(object sender, MouseEventArgs e)
    {
        internalTimer.Tag = DateTime.Now;
        internalTimer.Start();
    }

    private void RepeatingButton_MouseUp(object sender, MouseEventArgs e)
    {
        internalTimer.Stop();
        internalTimer.Interval = FirstDelay;
    }

    private void internalTimer_Tick(object sender, EventArgs e)
    {
        this.OnClick(e);
        TimeSpan elapsed = DateTime.Now - ((DateTime)internalTimer.Tag);
        if (elapsed.TotalMilliseconds < LoHiChangeTime)
        {
            internalTimer.Interval = LoSpeedWait;
        }
        else
        {
            internalTimer.Interval = HiSpeedWait;
        }
    }

    private Timer internalTimer;
}

Partout où vous avez un bouton, vous pouvez simplement le remplacer par un bouton répétitif et toutes les nouvelles fonctionnalités seront intégrées.

Prendre plaisir!

Sterren

3
Steztric

A un article récent de Fabulous Adventures in Coding fournit ce récit qui pourrait aider à répondre à votre question:

Un nombre surprenant de personnes a des convictions magiques sur la réponse exacte des applications aux entrées utilisateur de Windows. Je vous assure que ce n'est pas magique. La manière dont les interfaces utilisateur interactives sont construites dans Windows est assez simple. Lorsque quelque chose se passe, disons, un clic de souris sur un bouton, le système d'exploitation en prend note. À un moment donné, un processus demande au système d'exploitation "quelque chose d'intéressant est-il arrivé récemment?" et le système d'exploitation dit "pourquoi oui, quelqu'un a cliqué sur cette chose." Le processus prend ensuite les mesures appropriées pour cela. Ce qui se passe dépend du processus. il peut choisir d'ignorer le clic, de le gérer de manière spécifique ou de dire au système d'exploitation "continue et fais ce que la valeur par défaut est ce type d'événement". Tout cela est généralement basé sur le code le plus simple que vous puissiez voir:

while(GetMessage(&msg, NULL, 0, 0) > 0) 
{ 
  TranslateMessage(&msg); 
  DispatchMessage(&msg); 
}

C'est tout. Quelque part dans le cœur de chaque processus comportant un thread d'interface utilisateur se trouve une boucle qui ressemble remarquablement à celle-ci. Un appel reçoit le message suivant. Ce message est peut-être trop bas pour vous; Par exemple, cela peut indiquer qu'une touche avec un numéro de code de clavier particulier a été enfoncée. Vous voudrez peut-être que cela soit traduit par "la touche Numlock a été enfoncée". TranslateMessage fait cela. Il pourrait y avoir une procédure plus spécifique traitant de ce message. DispatchMessage transmet le message à la procédure appropriée.

Je tiens à souligner que ce n'est pas magique. C'est une boucle de temps. Il fonctionne comme toute autre boucle while en C que vous avez jamais vue . La boucle appelle à plusieurs reprises trois méthodes, dont chacune lit ou écrit un tampon et effectue une action avant de retourner. Si l'une de ces méthodes met longtemps à renvoyer (typiquement DispatchMessage est la longue, bien sûr puisqu'il s'agit de celle qui effectue le travail associé au message), alors devinez quoi? L'interface utilisateur ne récupère, ne traduit et ne distribue pas les notifications du système d'exploitation avant son retour.

3
Daniel Pryden

Remplacez la méthode OnMouseDown() dans votre formulaire, puis si vous appuyez sur le bouton souhaité, cela correspond à votre boucle. Exemple:

protected override void OnMouseDown(MouseEventArgs e)
{
    if (e.Button == MouseButtons.Left)
    {
        // this is your loop
    }
}

Ce n'est pas une boucle au sens traditionnel du terme, mais devrait fonctionner pour ce dont vous avez besoin.

1

Cela fait plusieurs années que j'ai posté ceci, mais quelqu'un l'a voté, cela est donc apparu dans mes notifications. Maintenant que j'ai beaucoup plus d'expérience lol, j'ai pensé voir si ce problème simple est aussi simple que cela puisse paraître, et c'était:

public partial class Form1 : Form
{
    private bool _isRunning;

    public Form1()
    {
        InitializeComponent();
        txtValue.Text = @"0";

        btnTest.MouseDown += (sender, args) =>
        {
            _isRunning = true;
            Run();
        };

        btnTest.MouseUp += (sender, args) => _isRunning = false;
    }

    private void Run()
    {
        Task.Run(() =>
        {
            while (_isRunning)
            {
                var currentValue = long.Parse(txtValue.Text);
                currentValue++;
                txtValue.Invoke((MethodInvoker) delegate
                {
                    txtValue.Text = currentValue.ToString();
                });
            }
        });
    }
}
1
Sinaesthetic

Vous devrez gérer l'événement MouseDown() pour votre formulaire, en utilisant l'argument MouseEventArgs pour déterminer le bouton sur lequel vous avez appuyé.

1
Bernard

vous pouvez utiliser l'événement mouseMove et vérifier si le bouton de la souris est maintenu enfoncé comme suit:

private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
    {
        if(e.Button==MouseButtons.Left)
        {
        //your code here
        }
    }
0
Roy Ermers

Basé sur la réponse de Steztric, une méthode d'extension avec quelques corrections de bugs et différentes options pour le taux d'augmentation.

/// <summary>
/// An extension method to add a repeat click feature to a button. Clicking and holding  on a button will cause it
/// to repeatedly fire. This is useful for up-down spinner buttons. Typically the longer the mouse is held, the
/// more quickly the click events are fired. There are different options when it comes to increasing the rate of
/// clicks:
/// 1) Exponential - this is the mode used in the NumericUpDown buttons. The first delay starts off around 650 ms
/// and each successive delay is multiplied by 75% of the current delay.
/// 2) Linear - the delay more slowly reaches the fastest repeat speed. Each successive delay subtracts a fixed
/// amount from the current delay. Decreases in delays occur half a second apart.
/// 3) Two Speed - this delay starts off at a slow speed, and then increases to a faster speed after a specified delay.
/// 4) Three Speed - the repeat speed can increase from slow, to medium, to fastest after a specified delay.
///
/// If repeating is added to a button that already has it, then it will be replaced with the new values.
/// </summary>
public static class RepeatingButtonEx {

    private static Hashtable ht = new Hashtable();
    private class Data {
        private static readonly System.Reflection.MethodInfo methodOnClick = null;
        static Data() {
            methodOnClick = typeof(Button).GetMethod("OnClick", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
        }

        public Button Button = null;
        private Timer Timer = new Timer();
        public double? GradientRate;
        public int? LinearGradient = null;
        public int FirstDelayMillis;
        public int FastestRepeatMillis;
        public int[] SwitchesMillis;
        public int[] SpeedsMillis;

        private DateTime lastEvent = DateTime.MinValue;
        private int millisCount = 0;
        private int currentSpeed = 0;
        private int waitSum = 0;

        public Data(Button button, double? gradientRate, int? linearGradient, int firstDelayMillis, int fastestRepeatMillis, int[] switchesMillis, int[] speedsMillis) {
            Button = button;
            GradientRate = gradientRate;
            LinearGradient = linearGradient;
            FirstDelayMillis = firstDelayMillis;
            FastestRepeatMillis = fastestRepeatMillis;
            SwitchesMillis = switchesMillis;
            SpeedsMillis = speedsMillis;
            Timer.Interval = firstDelayMillis;
            Timer.Tick += Timer_Tick;
            Button.MouseDown += Button_MouseDown;
            Button.MouseUp += Button_MouseUp;
            Button.MouseLeave += Button_MouseLeave;
        }

        void Button_MouseDown(object sender, MouseEventArgs e) {
            if (!Button.Enabled)
                return;

            lastEvent = DateTime.UtcNow;
            Timer.Start();
        }

        void Button_MouseUp(object sender, MouseEventArgs e) {
            Reset();
        }

        void Button_MouseLeave(object sender, EventArgs e) {
            Reset();
        }

        private void Reset() {
            Timer.Stop();
            Timer.Interval = FirstDelayMillis;
            millisCount = 0;
            currentSpeed = 0;
            waitSum = 0;
        }

        void Timer_Tick(object sender, EventArgs e) {
            if (!Button.Enabled) {
                Reset();
                return;
            }

            methodOnClick.Invoke(Button, new Object[] { EventArgs.Empty });
            //Button.PerformClick(); // if Button uses SetStyle(Selectable, false); then CanSelect is false, which prevents PerformClick from working.

            if (GradientRate.HasValue || LinearGradient.HasValue) {
                int millis = Timer.Interval;

                if (GradientRate.HasValue)
                    millis = (int) Math.Round(GradientRate.Value * millis);
                else if (LinearGradient.HasValue) {
                    DateTime now = DateTime.UtcNow;
                    var ts = now - lastEvent;
                    int ms = (int) ts.TotalMilliseconds;
                    millisCount += ms;
                    // only increase the rate every 500 milliseconds
                    // otherwise it appears too get to the maximum rate too quickly
                    if (millisCount >= 500) {
                        millis -= LinearGradient.Value;
                        millisCount -= 500;
                        lastEvent = now;
                    }
                }

                if (millis < FastestRepeatMillis)
                    millis = FastestRepeatMillis;

                Timer.Interval = millis;
            }
            else {
                if (currentSpeed < SpeedsMillis.Length) {
                    TimeSpan elapsed = DateTime.UtcNow - lastEvent; 
                    if (elapsed.TotalMilliseconds >= waitSum) {
                        waitSum += SwitchesMillis[currentSpeed];
                        Timer.Interval = SpeedsMillis[currentSpeed];
                        currentSpeed++;
                    }
                }
            }
        }

        public void Dispose() {
            Timer.Stop();
            Timer.Dispose();
            Button.MouseDown -= Button_MouseDown;
            Button.MouseUp -= Button_MouseUp;
            Button.MouseLeave -= Button_MouseLeave;
        }
    }

    ///<summary>The repeating speed becomes exponentially faster. This is the default behavior of the NumericUpDown control.</summary>
    ///<param name="button">The button to add the behavior.<param>
    ///<param name="firstDelayMillis">The delay before first repeat in milliseconds.</param>
    ///<param name="fastestRepeatMillis">The smallest delay allowed. Note: Masharling between the timer and the UI thread has an unavoidable limit of about 10 milliseconds.</param>
    ///<param name="gradientRate">The new interval is the current interval multiplied by the gradient rate.</param>
    public static void AddRepeatingExponential(this Button button, int firstDelayMillis = 500, int fastestRepeatMillis = 15, double gradientRate = 0.75) {
        AddRepeating(button, firstDelayMillis, fastestRepeatMillis, gradientRate, null, null, null);
    }

    ///<summary>The repeating speed becomes linearily faster.</param>
    ///<param name="button">The button to add the behavior.<param>
    ///<param name="firstDelayMillis">The delay before first repeat in milliseconds.</param>
    ///<param name="fastestRepeatMillis">The smallest delay allowed. Note: Masharling between the timer and the UI thread has an unavoidable limit of about 10 milliseconds.</param>
    ///<param name="linearGradient">If specified, the repeats gradually happen more quickly. The new interval is the current interval minus the linear gradient.</param>
    public static void AddRepeatingLinear(this Button button, int firstDelayMillis = 500, int fastestRepeatMillis = 50, int linearGradient = 25) {
        AddRepeating(button, firstDelayMillis, fastestRepeatMillis, null, linearGradient, null, null);
    }

    ///<summary>The repeating speed switches from the slow speed to the fastest speed after the specified amount of milliseconds.</summary>
    ///<param name="button">The button to add the behavior.<param>
    ///<param name="firstDelayMillis">The delay before first repeat in milliseconds.</param>
    ///<param name="fastestRepeatMillis">The smallest delay allowed. Note: Masharling between the timer and the UI thread has an unavoidable limit of about 10 milliseconds.</param>
    ///<param name="slowRepeatMillis">The delay in milliseconds between repeats when in the slow repeat state.</param>
    ///<param name="slowToFastestSwitchMillis">The delay in milliseconds before switching from the slow repeat speed to the fastest repeat speed.</param>
    public static void AddRepeatingTwoSpeed(this Button button, int firstDelayMillis = 500, int fastestRepeatMillis = 100, int slowRepeatMillis = 300, int slowToFastestSwitchMillis = 2000) {
        AddRepeating(button, firstDelayMillis, fastestRepeatMillis, null, null, new[] { slowRepeatMillis, fastestRepeatMillis }, new [] { slowToFastestSwitchMillis, 0 });
    }

    ///<summary>The repeating speed switches from the slow to medium to fastest at speed switch interval specified.</summary>
    ///<param name="button">The button to add the behavior.<param>
    ///<param name="firstDelayMillis">The delay before first repeat in milliseconds.</param>
    ///<param name="fastestRepeatMillis">The smallest delay allowed. Note: Masharling between the timer and the UI thread has an unavoidable limit of about 10 milliseconds.</param>
    ///<param name="slowRepeatMillis">The delay in milliseconds between repeats when in the slow repeat state.</param>
    ///<param name="mediumRepeatMillis">The delay in milliseconds between repeats when in the medium repeat state.</param>
    ///<param name="speedSwitchMillis">The delay in milliseconds before switching from one speed state to the next speed state.</param>
    public static void AddRepeatingThreeSpeed(this Button button, int firstDelayMillis = 500, int fastestRepeatMillis = 75, int slowRepeatMillis = 300, int mediumRepeatMillis = 150, int speedSwitchMillis = 2000) {
        AddRepeating(button, firstDelayMillis, fastestRepeatMillis, null, null, new[] { slowRepeatMillis, mediumRepeatMillis, fastestRepeatMillis }, new [] { speedSwitchMillis, speedSwitchMillis, 0 });
    }

    private static void AddRepeating(this Button button, int firstDelayMillis, int fastestRepeatMillis, double? gradientRate, int? linearGradient, int[] speedsMillis, int[] switchesMillis) {
        Data d = (Data) ht[button];
        if (d != null)
            RemoveRepeating(button);

        d = new Data(button, gradientRate, linearGradient, firstDelayMillis, fastestRepeatMillis, switchesMillis, speedsMillis);
        ht[button] = d;
        button.Disposed += delegate {
            RemoveRepeating(button);
        };
    }

    ///<summary>Removes the repeating behavior from the button.</summary>
    public static void RemoveRepeating(this Button button) {
        Data d = (Data) ht[button];
        if (d == null)
            return;

        ht.Remove(button);
        d.Dispose();
    }
}
0
Loathing

RepeatButton est parfait pour cela:

<RepeatButton Delay="1000" Interval="500" HorizontalAlignment="Left" Content="+" Click="IncreaseButton_Click"/>

private void IncreaseButton_Click(object sender, RoutedEventArgs e)
{
    value1++;
}
0
Arglist

Semblable à la solution de Timwi ci-dessus, sauf que vous utilisez async/await pour IO asynchrone _ et lock pour la synchronisation d'un état quelconque ...

using System;
using System.Windows.Forms;
using System.Threading.Tasks;

namespace Foo {
    partial class Form1: Form {
        private static readonly object mousePressLock = new object();
        private bool mousePressed;
        private Task task;

        private async Task MouseAction(Action action) {
            while (true) {
                lock (mousePressLock) {
                    if (mousePressed)
                        action();
                    else
                        break;
                }
                await Task.Delay(100).ConfigureAwait(false);
            }
        }

        private void PnlTranslate_Paint(object sender, PaintEventArgs e) {
        }

        private void Up_MouseUp(object sender, MouseEventArgs e) {
            lock (mousePressLock) { mousePressed = false; }
            task.Wait();
        }

        private void Up_MouseDown(object sender, MouseEventArgs e) {
            lock (mousePressLock) { mousePressed = true; }
            int cnt = 0;
            task = MouseAction(() => {
                Console.WriteLine($"mouse up action {++cnt}");
            });
        }

        public Form1() {
            InitializeComponent();
            mousePressed = false;
            task = null;
        }
    }
}

Notez également l'appel ConfigureAwait(false). Je suis tombé dans une impasse sans ce b.c. les tâches se battaient pour être sur le même fil. C'était tellement énervant.

0
solstice333