私を正しい方向に向けることができますか?フォームボタンが押されている間にループをトリガーしようとしています。
//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());
};
もちろん、イベントハンドラーをラムダではなくメソッドにしたい場合は、すべての変数をフィールドに変換する必要があります。
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++;
}
ここで読んだことに刺激を受け、RepeatingButtonという独自のボタンクラスを作成することにしました。最初のクリックで500ミリ秒待機し、2秒まで300ミリ秒を繰り返し、その後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;
}
ボタンがあればどこでも、それを繰り返しボタンに置き換えるだけで、すべての新機能が組み込まれます。
楽しい!
星
A Fabulous Adventures in Codingの最近の記事 はこの物語を提供しており、質問への回答に役立つ可能性があります。
驚くほど多くの人々が、アプリケーションがWindowsでのユーザー入力にどのように応答するかについて不思議な信念を持っています。それは魔法ではないことを保証します。対話型ユーザーインターフェイスがWindowsで構築される方法は、非常に簡単です。何かが発生すると、たとえば、ボタンをマウスでクリックすると、オペレーティングシステムによってそのことが記録されます。ある時点で、プロセスはオペレーティングシステムに「最近何か興味深いことが起こりましたか?」と尋ねます。そしてオペレーティングシステムは「なぜそうなのか、誰かがこれをクリックした」と言います。次に、プロセスはそれに適したアクションを実行します。何が起こるかはプロセス次第です。クリックを無視するか、独自の特別な方法で処理するか、またはオペレーティングシステムに「その種類のイベントのデフォルトの処理を行って」と指示することができます。これらすべては、通常、これまでにない最も単純なコードによって駆動されます。
while(GetMessage(&msg, NULL, 0, 0) > 0) { TranslateMessage(&msg); DispatchMessage(&msg); }
それでおしまい。 UIスレッドを持つすべてのプロセスの中心のどこかに、このループのように見えるループがあります。 1つの呼び出しが次のメッセージを受け取ります。そのメッセージはあなたにはレベルが低すぎるかもしれません。たとえば、特定のキーボードコード番号のキーが押されたと言うことができます。これを「numlockキーが押された」に変換したい場合があります。 TranslateMessageはそれを行います。このメッセージを処理するより具体的な手順があるかもしれません。 DispatchMessageは、メッセージを適切なプロシージャに渡します。
これは魔法ではないことを強調したいと思います。これは、whileループです。 これまでに見た他のCのループと同じように実行されます 。ループは3つのメソッドを繰り返し呼び出します。それぞれのメソッドはバッファーの読み取りまたは書き込みを行い、戻る前に何らかのアクションを実行します。これらのメソッドの1つが戻るのに長い時間がかかる場合(通常、DispatchMessageは、実際にはメッセージに関連付けられた作業を実際に行っているため、実行時間の長いメソッドです)、どうなるでしょうか。 UIは、オペレーティングシステムからの通知をフェッチ、変換、またはディスパッチしません。
フォームのOnMouseDown()
メソッドをオーバーライドし、必要なボタンが押された場合、ループと同じになります。例:
protected override void OnMouseDown(MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
// this is your loop
}
}
これは従来の意味でのループではありませんが、必要に応じて機能するはずです。
私がこれを投稿してから数年になりますが、誰かがそれを賛成したので、私の通知にポップアップ表示されました。私はもっと多くの経験をしたので、私はこの単純な問題がそれが聞こえるほど単純であるかどうかを確認すると思いました、そしてそれはそうでした:
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();
});
}
});
}
}
フォームのMouseDown()
イベントを処理し、MouseEventArgs
引数を使用して、どのボタンが押されたかを把握する必要があります。
次のように、mouseMoveイベントを使用して、マウスボタンが押されているかどうかを確認できます。
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
if(e.Button==MouseButtons.Left)
{
//your code here
}
}
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();
}
}
RepeatButton はそのために最適です:
<RepeatButton Delay="1000" Interval="500" HorizontalAlignment="Left" Content="+" Click="IncreaseButton_Click"/>
private void IncreaseButton_Click(object sender, RoutedEventArgs e)
{
value1++;
}
上記の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)
の呼び出しにも注意してください。私はそれなしでデッドロックに遭遇しました。タスクは同じスレッド上にいるために戦っていました。それはとても迷惑でした。