2010-11-08 18 views
13

Czy możesz wskazać mi właściwy kierunek? Próbuję uruchomić pętlę, gdy przycisk formularza jest wciśnięty.C# jak zapętlić, gdy przycisk myszy jest wciśnięty

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

A potem oczywiście zatrzymać pętli przy zwolnieniu przycisku

+0

http://pmichaels.net/201/01/01/using-a-repeatbutton-to-rapid-increment-values ​​/ –

Odpowiedz

23

Aby unikać tematów można dodać składnik Timer na formularzu/kontroli i po prostu ją włączyć na myszy i wyłączyć ją na mysz w górę. Następnie umieść kod, który normalnie umieszczasz w pętli w zdarzeniu Timer_Tick.Jeśli chcesz użyć System.Timers.Timer, możesz zamiast tego użyć zdarzenia Timer.Elapsed.

Przykład (przy użyciu 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; 
}  
+1

Dobra odpowiedź: Zapewnia alternatywę dla mojego rozwiązania opartego na wątkach. +1. – Timwi

+0

Istnieje duża szansa, że ​​chcesz sprawdzić, czy '(e.Button == MouseButtons.Left)', w przeciwnym razie kończy się wykonywanie pętli w LeftClick, lub RightClick, lub MiddleClick, lub dowolnym innym przycisku myszy. Zazwyczaj przyciski powinny reagować tylko na lewy przycisk myszy. – abelenky

+0

Problem z 'Timerem' polega na tym, że nie jest on dokładny i jest znany. Spróbuj ustawić timer na znany milisekundowy "interval", a następnie policz liczbę ticków i porównaj go ze stoperem "elapsed_miliesseconds/interval". Zobaczysz zauważalną różnicę w uzyskiwanych wartościach. Ale jeśli OP nie chce wysokiej precyzji, jest to dobre rozwiązanie. – Gondil

1

zastąpić metodę OnMouseDown() w swojej formie, a następnie, jeśli zostanie naciśnięty przycisk chcesz, że będzie równy swoją pętlę. Przykład:

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

To nie jest pętla w tradycyjnym sensie, ale powinna działać na to, czego potrzebujesz.

+1

to w rzeczywistości nie działa. jeśli przytrzymasz przycisk, uruchamia się tylko raz. następnie uruchamia się ponownie po zwolnieniu przycisku. – Sinaesthetic

1

Będziesz musiał zająć się zdarzeniem MouseDown() dla swojego formularza, używając argumentu MouseEventArgs, aby dowiedzieć się, który przycisk został naciśnięty.

+0

Dlaczego upadek? – Bernard

+0

To samo dotyczy moich ... kogoś w górze, kogoś w dół ... źle, sprawiają, że +1 dla ciebie –

+1

Ta odpowiedź w ogóle nie odpowiada na pytanie. Gdzie jest pętla? – Timwi

11

Możesz użyć nici do liczenia i zatrzymać wątek po zwolnieniu myszy. Poniższa pracował dobrze dla mnie:

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

Oczywiście, jeśli chcesz, obsługi zdarzeń, aby być metody zamiast lambda, trzeba będzie włączyć wszystkie zmienne do pól.

+0

To zadziałało idealnie dla mnie - świetne rozwiązanie. – CramerTV

3

recent article from Fabulous Adventures in Coding zapewnia tę narrację, która może pomóc odpowiedzieć na pytanie:

Zaskakująca liczba ludzi ma magiczne przekonania o tym, jak dokładnie aplikacje odpowiedzi na dane wejściowe użytkownika w systemie Windows. Zapewniam cię, że to nie magia. Sposób, w jaki interaktywne interfejsy użytkownika są tworzone w systemie Windows, jest dość prosty. Kiedy coś się stanie, powiedzmy kliknięciem myszki na przycisk, system operacyjny zanotuje to. W pewnym momencie proces prosi o system operacyjny "czy ostatnio wydarzyło się coś ciekawego?" a system operacyjny mówi "dlaczego tak, ktoś kliknął to coś". Następnie proces wykonuje odpowiednie działania. To, co się dzieje, zależy od tego procesu; może zignorować kliknięcie, obchodzić się z nim w specjalny sposób lub powiedzieć systemowi operacyjnemu, aby "robił wszystko, co domyślne dla tego rodzaju zdarzenia". Wszystko to jest zwykle prowadzone przez najprostszy kod, jaki kiedykolwiek widzisz:

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

To wszystko. Gdzieś w sercu każdego procesu, który ma wątek UI, jest pętla, która wygląda podobnie jak ta. Jedno połączenie otrzymuje następną wiadomość. Ta wiadomość może być dla ciebie zbyt niska; na przykład może to oznaczać, że został naciśnięty klawisz z określonym numerem kodu klawiatury. Możesz to przetłumaczyć na "naciśnięcie klawisza numlock". TranslateMessage to robi. Może istnieć jakaś bardziej szczegółowa procedura, która zajmuje się tym komunikatem. DispatchMessage przekazuje wiadomość do odpowiedniej procedury.

Chcę podkreślić, że to nie jest magia. To jest pętla. It runs like any other while loop in C that you've ever seen. Pętla wielokrotnie wywołuje trzy metody, z których każda odczytuje lub zapisuje bufor i podejmuje pewne działania przed powrotem. Jeśli powrót do jednej z tych metod zajmuje dużo czasu (zazwyczaj DispatchMessage jest oczywiście długotrwały, ponieważ to on faktycznie wykonuje pracę związaną z wiadomością), to zgadnij co? Interfejs użytkownika nie pobiera, nie tłumaczy ani nie wysyła powiadomień z systemu operacyjnego do czasu, w którym powróci.

+2

Czy ta ostatnia opieka zaoferuje wyjaśnienie? –

+1

Pewnie. Gdzie jest odpowiedź na pytanie? – Timwi

+3

@Timwi: Odpowiedź brzmi: "Twoje pytanie zależy od niedokładnego założenia." Zamiast stwierdzenia tego bez ogródek, przedstawiłem wyjaśnienie podstawowego mechanizmu, który, jak sądziłem, pomógłby wyjaśnić myślenie PO. Ponieważ pytanie wyraźnie pyta "Czy możesz wskazać mi właściwy kierunek?" wydawało się to odpowiednim sposobem na zrobienie tego. Na pewno możesz się z tym nie zgodzić, ale doceniam to, że wyjaśniłeś swoje rozumowanie. –

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

    } 
+0

jest to odpowiedź DOGGETT i działa! – fahdovski

3

I była inspirowana przez co czytałem tutaj i postanowiłem napisać własną klasę przycisk o nazwie RepeatingButton. Przy pierwszym kliknięciu czeka na 500 ms, a następnie powtarza do 300 ms aż do 2 sekund, a następnie powtarza co 100 ms (to znaczy wykorzystuje przyspieszenie).

Oto kod;

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

Gdziekolwiek masz przycisk, można po prostu zastąpić go powtarzając przycisku i będzie po prostu mieć wszystkie nowe funkcje wbudowane w.

Enjoy!

Sterren

0

podstawie odpowiedzi Steztric za metodę rozszerzenia, z kilkoma poprawkami i różnych opcji dla tempa wzrostu.

/// <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

można użyć mouseMove zdarzenie i sprawdzić, czy przycisk myszy zostanie przytrzymany jak:

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

RepeatButton jest idealnym miejscem na to:

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

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

Minęło kilka lat od czasu, kiedy pisał ale ktoś go przegłosował, więc pojawił się w moich powiadomieniach. Teraz, gdy mam dużo więcej doświadczeń, pomyślałem, że zobaczę, czy ten prosty problem jest tak prosty, jak się wydaje, i był:

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(); 
       }); 
      } 
     }); 
    } 
} 
Powiązane problemy