С# как петля при нажатой кнопке мыши

Можете ли вы указать мне в правильном направлении? Я пытаюсь запустить цикл, пока кнопка формы нажата.

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

И тогда, конечно, остановка цикла при отпускании кнопки

Ответы

Ответ 1

Чтобы избежать использования потоков, вы можете добавить компонент Timer в свою форму/элемент управления и просто включить его с помощью мыши и отключить его мышь вверх. Затем поместите код, который вы обычно помещаете внутри цикла в событие Timer_Tick. Если вы хотите использовать System.Timers.Timer, вы можете использовать событие Timer.Elapsed.

Пример (с использованием 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;
}       

Ответ 2

Вы можете использовать поток для подсчета и остановить поток при освобождении мыши. Для меня работали хорошо:

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

Конечно, если вы хотите, чтобы обработчики событий были методами, а не lambdas, вам придется превратить все переменные в поля.

Ответ 3

    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++;

    }

Ответ 4

A недавняя статья из Fabulous Adventures in Coding содержит этот рассказ, который может помочь ответить на ваш вопрос:

У удивительного количества людей есть волшебные убеждения о том, как именно приложения реагируют на пользовательские входы в Windows. Уверяю вас, что это не волшебство. Способ создания интерактивных пользовательских интерфейсов в Windows довольно прост. Когда что-то происходит, скажем, щелчком мыши по кнопке, операционная система делает это. В какой-то момент процесс спрашивает операционную систему: "В последнее время произошло что-то интересное?" и операционная система говорит "почему да, кто-то щелкнул эту штуку". Затем этот процесс делает все возможное для этого. Что происходит, зависит от процесса; он может выбрать игнорировать щелчок, обрабатывать его по-своему, или сообщить операционной системе "идти вперед и делать все, что по умолчанию для такого рода событий". Все это, как правило, связано с простейшим кодом, который вы когда-либо видели:

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

Что это. Где-то в центре каждого процесса, в котором есть поток пользовательского интерфейса, есть цикл, который выглядит так же, как этот. Один вызов получает следующее сообщение. Это сообщение может быть слишком низким для вас; например, можно сказать, что был нажат ключ с определенным номером кода клавиатуры. Возможно, вы захотите, чтобы это было переведено на "клавиша numlock". TranslateMessage делает это. Может возникнуть какая-то более конкретная процедура, связанная с этим сообщением. DispatchMessage передает сообщение с соответствующей процедурой.

Хочу подчеркнуть, что это не волшебство. Это цикл while. Он работает как любой другой цикл while в C, который вы когда-либо видели. Цикл повторно вызывает три метода, каждый из которых считывает или записывает буфер и предпринимает некоторые действия перед возвратом. Если один из этих методов требует много времени, чтобы вернуться (обычно DispatchMessage является длительным, конечно, поскольку он фактически выполняет работу, связанную с сообщением), тогда угадайте, что? Пользовательский интерфейс не выводит, не переводит или не отправляет уведомления из операционной системы до тех пор, пока он не вернется.

Ответ 5

Я был вдохновлен тем, что я прочитал здесь, и решил написать свой собственный класс кнопок под названием RepeatingButton. При первом нажатии он ждет 500 мс, а затем повторяет каждые 300 мс до 2 секунд, затем повторяет каждые 100 мс (то есть использует ускорение).

Вот код:

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

В любом месте, где у вас есть кнопка, вы можете просто заменить ее повторяющейся кнопкой, и она будет иметь все новые функциональные возможности.

Наслаждайтесь!

Штеррен

Ответ 6

Переопределите метод OnMouseDown() в вашей форме, а затем, если нажата кнопка, которая будет равна вашему циклу. Пример:

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

Это не цикл в традиционном смысле, но должен работать на то, что вам нужно.

Ответ 7

Вам нужно обработать событие MouseDown() для вашей формы, используя аргумент MouseEventArgs, чтобы выяснить, какая кнопка была нажата.

Ответ 8

На основе ответа Steztric - метод расширения с несколькими исправлениями ошибок и различными параметрами для увеличения.

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

Ответ 9

вы можете использовать событие mouseMove и проверить, удерживается ли указатель мыши как:

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

Ответ 10

RepeatButton идеально подходит для этого:

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

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

Ответ 11

Прошло несколько лет с тех пор, как я опубликовал это, но кто-то поддержал его, чтобы он появился в моих уведомлениях. Теперь, когда у меня появилось намного больше опыта, я подумал, что посмотрю, прост ли эта простая проблема, как это звучит, и это было:

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