Можете ли вы указать мне в правильном направлении? Я пытаюсь запустить цикл, пока кнопка формы нажата.
//pseudocode
While (button1 is pressed)
value1 += 1
И тогда, конечно, остановка цикла при отпускании кнопки
Можете ли вы указать мне в правильном направлении? Я пытаюсь запустить цикл, пока кнопка формы нажата.
//pseudocode
While (button1 is pressed)
value1 += 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;
}
Вы можете использовать поток для подсчета и остановить поток при освобождении мыши. Для меня работали хорошо:
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, вам придется превратить все переменные в поля.
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++;
}
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 является длительным, конечно, поскольку он фактически выполняет работу, связанную с сообщением), тогда угадайте, что? Пользовательский интерфейс не выводит, не переводит или не отправляет уведомления из операционной системы до тех пор, пока он не вернется.
Я был вдохновлен тем, что я прочитал здесь, и решил написать свой собственный класс кнопок под названием 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;
}
В любом месте, где у вас есть кнопка, вы можете просто заменить ее повторяющейся кнопкой, и она будет иметь все новые функциональные возможности.
Наслаждайтесь!
Штеррен
Переопределите метод OnMouseDown() в вашей форме, а затем, если нажата кнопка, которая будет равна вашему циклу. Пример:
protected override void OnMouseDown(MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
// this is your loop
}
}
Это не цикл в традиционном смысле, но должен работать на то, что вам нужно.
Вам нужно обработать событие MouseDown() для вашей формы, используя аргумент MouseEventArgs, чтобы выяснить, какая кнопка была нажата.
На основе ответа 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();
}
}
вы можете использовать событие mouseMove и проверить, удерживается ли указатель мыши как:
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
if(e.Button==MouseButtons.Left)
{
//your code here
}
}
RepeatButton идеально подходит для этого:
<RepeatButton Delay="1000" Interval="500" HorizontalAlignment="Left" Content="+" Click="IncreaseButton_Click"/>
private void IncreaseButton_Click(object sender, RoutedEventArgs e)
{
value1++;
}
Прошло несколько лет с тех пор, как я опубликовал это, но кто-то поддержал его, чтобы он появился в моих уведомлениях. Теперь, когда у меня появилось намного больше опыта, я подумал, что посмотрю, прост ли эта простая проблема, как это звучит, и это было:
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();
});
}
});
}
}