代码之家  ›  专栏  ›  技术社区  ›  Sinaesthetic

C按下鼠标按钮时如何循环

  •  13
  • Sinaesthetic  · 技术社区  · 14 年前

    你能给我指出正确的方向吗?我正试图让一个循环在按下窗体按钮时触发。

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

    当然,当按钮松开时停止循环

    12 回复  |  直到 5 年前
        1
  •  25
  •   Max von Hippel MD. Sahib Bin Mahboob    9 年前

    为了避免使用线程,可以添加 Timer 窗体/控件上的组件,只需在鼠标向下时启用它,在鼠标向上时禁用它。然后将通常放入循环中的代码放入计时器勾号事件中。如果要使用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
  •  11
  •   Timwi    14 年前

    您可以使用一个线程来进行计数,并在释放鼠标时停止线程。以下内容对我来说非常有用:

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

    当然,如果希望事件处理程序是方法而不是lambda,则必须将所有变量转换为字段。

        3
  •  7
  •   fahdovski    13 年前
        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
  •  3
  •   Daniel Pryden    14 年前

    recent article from Fabulous Adventures in Coding 提供这种叙述,可能有助于回答您的问题:

    令人惊讶的是,许多人对应用程序如何准确地响应Windows中的用户输入有着不可思议的信念。我向你保证这不是魔法。在Windows中构建交互式用户界面的方式非常简单。当发生什么事情时,比如说,鼠标点击一个按钮,操作系统就会记录下来。在某个时刻,一个进程询问操作系统“最近有什么有趣的事情发生吗?”操作系统会说“为什么是的,有人点击了这个东西”,然后这个过程会做任何合适的操作。所发生的一切取决于进程;它可以选择忽略单击,以自己特殊的方式处理它,或者告诉操作系统“继续执行,为这种事件做任何默认的事情”。所有这些通常都是由您将看到的一些最简单的代码驱动的:

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

    就这样。在每个有UI线程的进程的中心,都有一个看起来非常像这个线程的循环。一个电话接下一条信息。对于您来说,该消息的级别可能太低;例如,它可能会说按下了具有特定键盘代码的键。您可能希望将其转换为“已按下numlock键”。翻译消息就是这样。可能有一些更具体的过程来处理这个消息。DispatchMessage将消息传递给相应的过程。

    我想强调的是,这不是魔法。这是一个while循环。 It runs like any other while loop in C that you've ever seen . 循环反复调用三个方法,每个方法都读取或写入缓冲区,并在返回前执行一些操作。如果其中一个方法需要很长时间才能返回(通常DispatchMessage是长期运行的方法,因为它实际上是执行与消息相关联的工作的方法),那么猜猜怎么着?在操作系统返回通知之前,用户界面不会从操作系统获取、转换或分派通知。

        5
  •  3
  •   Steztric    12 年前

    我被我在这里读到的内容所启发,决定写我自己的button类,叫做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
  •  1
  •   Richard J. Ross III    14 年前

    覆盖 OnMouseDown() 方法,如果按下所需按钮,则等于循环。例子:

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

    这不是传统意义上的循环,而是应该为你所需要的而工作。

        7
  •  1
  •   Bernard    14 年前

    你需要处理 MouseDown() 窗体的事件,使用 MouseEventArgs 用于确定按下哪个按钮的参数。

        8
  •  1
  •   Sinaesthetic    7 年前

    我已经好几年没发过这个了,但是有人投了赞成票,所以它在我的通知中出现了。现在我有了更多的LOL经验,我想我会看看这个简单的问题是否像听起来那么简单,它是:

    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();
                    });
                }
            });
        }
    }
    
        9
  •  0
  •   Loathing    9 年前

    基于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();
        }
    }
    
        10
  •  0
  •   Roy Ermers    8 年前

    您可以使用mousemove事件并检查mousebutton是否按下如下所示:

    private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
        {
            if(e.Button==MouseButtons.Left)
            {
            //your code here
            }
        }
    
        11
  •  0
  •   Arglist    8 年前

    RepeatButton 非常适合:

    <RepeatButton Delay="1000" Interval="500" HorizontalAlignment="Left" Content="+" Click="IncreaseButton_Click"/>
    
    private void IncreaseButton_Click(object sender, RoutedEventArgs e)
    {
        value1++;
    }
    
        12
  •  0
  •   solstice333    5 年前

    类似于上面Timwi的解决方案,除了使用 async/await 对于异步IO和 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) 打电话。我在没有BC的情况下陷入了僵局,任务是为了保持一致。真烦人。