C # как зациклить, когда кнопка мыши удерживается нажатой - PullRequest
13 голосов
/ 08 ноября 2010

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

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

И затем, конечно, прекратить зацикливание при отпускании кнопки

Ответы [ 12 ]

25 голосов
/ 08 ноября 2010

Чтобы избежать использования потоков, вы можете добавить компонент 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;
}       
11 голосов
/ 08 ноября 2010

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

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

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

7 голосов
/ 24 сентября 2011
    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++;

    }
3 голосов
/ 24 мая 2012

Я был вдохновлен тем, что я здесь прочитал, и решил написать свой собственный класс кнопок, называемый 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;
}

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

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

Sterren

3 голосов
/ 08 ноября 2010

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

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

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

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

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

1 голос
/ 15 июня 2017

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

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 голос
/ 08 ноября 2010

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

1 голос
/ 08 ноября 2010

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

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

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

0 голосов
/ 11 февраля 2019

Аналогично решению Тимви выше, за исключением использования async/await для асинхронного ввода-вывода и lock для синхронизации для некоторого состояния ...

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

Также обратите внимание на звонок ConfigureAwait(false). Я попал в тупик без этого б.с. задачи боролись, чтобы быть в одной теме. Это было так раздражает.

0 голосов
/ 17 июня 2016

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

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

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