如何在WinForms中忽略用户单击?

如何在WinForms中忽略用户单击?,winforms,multithreading,background-process,messages,Winforms,Multithreading,Background Process,Messages,当用户单击一个按钮时,它会启动一些任务。我不想阻止主应用程序线程,所以我在一个单独的线程中运行它。现在我需要禁止用户在我的任务完成之前单击按钮 我可以设定 button.Enabled = false; ,但我正在寻找一些方法来忽略对它的点击 我可以添加一些签入单击事件处理程序: if (executingThread != null) return; ,但我必须为每个处理者做这件事,这是个坏主意 我知道有一些方法可以过滤用户的消息。你能告诉我怎么做吗?我不想过滤掉所有消息,因为其他一些按钮

当用户单击一个按钮时,它会启动一些任务。我不想阻止主应用程序线程,所以我在一个单独的线程中运行它。现在我需要禁止用户在我的任务完成之前单击按钮

我可以设定

button.Enabled = false;
,但我正在寻找一些方法来忽略对它的点击

我可以添加一些签入单击事件处理程序:

if (executingThread != null) return;
,但我必须为每个处理者做这件事,这是个坏主意

我知道有一些方法可以过滤用户的消息。你能告诉我怎么做吗?我不想过滤掉所有消息,因为其他一些按钮必须保持可点击状态,我需要过滤掉特定控件(按钮、网格等)的消息

解决方案

internal class MessagesFilter: IMessageFilter
{
    private readonly IntPtr ControlHandler;

    private const int WM_KEYUP = 0x0101;

    public MessagesFilter(IntPtr ControlHandler)
    {
        this.ControlHandler = ControlHandler;
    }

    #region IMessageFilter Members

    public bool PreFilterMessage(ref Message m)
    {
        // TODO:  Add MessagesFilter.PreFilterMessage implementation
        if (m.Msg == WM_KEYUP)
        {
            if (m.HWnd == ControlHandler)
            {
                Keys k = ((Keys) ((int) m.WParam));

                if (k == Keys.Enter)                    
                    return true;
            }
        }

        return false;
    }

    #endregion
}

和往常一样,UI的呈现方式应该让用户理解应用程序在做什么,并且
应该使用UI元素与用户交谈

正如Adam Houldsworth所建议的那样,我也希望保持
按钮处于禁用或启用状态,但我还建议按钮的标题应向用户传达一个信息,即新线程启动时长处理正在进行中。。因此
按钮的标题应立即更改对于类似于
“处理..请等待…”
(除了被禁用或即使您想保持启用),然后如果您保持按钮启用,只需检查按钮
上的按钮标题(或isProcessing bool标志),单击事件返回,如果它说“处理..请等待…”或(isProcessing==true)

许多网站帮助用户上传
文件/图像
上传
按钮的标题更改为
“上传..请稍候…”
通知用户等待上传完成,此外,一些网站还禁用上传按钮,以便用户无法再次单击上传按钮

当线程完成长时间处理时,您还需要将标题恢复为正常

可能还有其他先进的方法,但我们的想法是让它尽可能简单和基本


查看此示例,其中显示了在多线程时禁用按钮。

+1了解到目前为止的所有建议。正如
CSharpVJ
所建议的-
我的想法是通过更改按钮的标题来额外通知用户,使UI设计更直观

这可以通过Winforms中的Backgroundworker组件优雅地实现[No Hassels code]。只需复制粘贴并点击F5(在创建一个带有按钮和标签的新Winforms项目后)

您不必在此处检查与按钮相关的任何内容。所有内容都将由相应的事件处理程序处理。您只需在保留事件处理程序中执行正确的操作。请尝试

using System.ComponentModel;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
    public partial class Form3 : Form
    {
        private BackgroundWorker _worker;

        public Form3()
        {
            InitializeComponent();
            InitWorker();
        }

        private void InitWorker()
        {
            if (_worker != null)
            {
                _worker.Dispose();
            }

            _worker = new BackgroundWorker
            {
                WorkerReportsProgress = true,
                WorkerSupportsCancellation = true
            };
            _worker.DoWork += DoWork;
            _worker.RunWorkerCompleted += RunWorkerCompleted;
            _worker.ProgressChanged += ProgressChanged;
        }

        /// do time consuming work here... 
        void DoWork(object sender, DoWorkEventArgs e)
        {
            int highestPercentageReached = 0;
            if (_worker.CancellationPending)
            {
                e.Cancel = true;
            }
            else
            {
                double i = 0.0d;

                for (i = 0; i <= 199990000; i++)
                {
                    // Report progress as a percentage of the total task.
                    var percentComplete = (int)(i / 199990000 * 100);
                    if (percentComplete > highestPercentageReached)
                    {
                        highestPercentageReached = percentComplete;
                        // Report UI abt the progress  
                        _worker.ReportProgress(percentComplete);
                        _worker.CancelAsync();
                    }
                }

            }
        }

        void RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            button1.Enabled = true;
            if (e.Cancelled)
            {
                // Display some message to the user that task has been
                // cancelled
                label1.Text = "Cancelled the operation";
            }
            else if (e.Error != null)
            {
                // Do something with the error
            }

            button1.Text = "Start again";
        }

        void ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            label1.Text =  string.Format("Result {0}: Percent {1}",e.UserState, e.ProgressPercentage);
        }

        private void OnStartClick(object sender, System.EventArgs e)
        {
            _worker.RunWorkerAsync();
            button1.Text = "Processing started...";
            button1.Enabled = false;
        }

    }
}
使用System.ComponentModel;
使用System.Windows.Forms;
命名空间Windows窗体应用程序1
{
公共部分类表单3:表单
{
私人背景工人;
公共表格3()
{
初始化组件();
InitWorker();
}
私有void InitWorker()
{
如果(_worker!=null)
{
_worker.Dispose();
}
_worker=新的后台工作人员
{
WorkerReportsProgress=true,
WorkerSupportsScanCellation=真
};
_worker.DoWork+=DoWork;
_worker.RunWorkerCompleted+=RunWorkerCompleted;
_worker.ProgressChanged+=ProgressChanged;
}
///在这里做耗时的工作。。。
无效DoWork(对象发送方,DoWorkEventArgs e)
{
int highestPercentageReached=0;
如果(_worker.CancellationPending)
{
e、 取消=真;
}
其他的
{
双i=0.0d;
对于(i=0;i最高百分比)
{
highestPercentageReached=完成百分比;
//报告进度
_工人报告进度(完成百分比);
_worker.CancelAsync();
}
}
}
}
void RunWorkerCompleted(对象发送方,RunWorkerCompletedEventArgs e)
{
按钮1.启用=真;
如果(如已取消)
{
//向用户显示任务已完成的消息
//取消
label1.Text=“已取消操作”;
}
否则如果(例如错误!=null)
{
//对这个错误做点什么
}
按钮1.Text=“重新开始”;
}
void ProgressChanged(对象发送方,progresschangedventargs e)
{
label1.Text=string.Format(“结果{0}:百分比{1}”,e.UserState,e.ProgressPercentage);
}
private void OnStartClick(对象发送者,System.EventArgs e)
{
_worker.RunWorkerAsync();
按钮1.Text=“处理已开始…”;
按钮1.启用=错误;
}
}
}

如其他答案所述,可能有比您所要求的更好的解决方案

要直接回答您的问题,请查看

创建筛选器以使其抑制不需要的鼠标消息,必要时使用
Application.AddMessageFilter()
应用它

以下内容(这可能需要编译…):


CSharpVJ,Adam Houldsworth感谢您的回复,但我需要我所问的。告诉用户长进程运行、将游标更改为WaitCursor等等-很清楚。我可以检查正在执行的线程,而不是检查按钮的标题,这是个坏主意。
public class MouseButtonFilter : IMessageFilter
{

    private const int WM_LBUTTONDOWN            = 0x0201;
    private const int WM_LBUTTONUP              = 0x0202;
    private const int WM_LBUTTONDBLCLK          = 0x0203;
    private const int WM_RBUTTONDOWN            = 0x0204;
    private const int WM_RBUTTONUP              = 0x0205;
    private const int WM_RBUTTONDBLCLK          = 0x0206;
    private const int WM_MBUTTONDOWN            = 0x0207;
    private const int WM_MBUTTONUP              = 0x0208;

    bool IMessageFilter.PreFilterMessage(ref Message m)
    {
        switch (m.Msg)
        {
            case WM_LBUTTONDOWN:
            /* case ... (list them all here; i'm being lazy) */
            case WM_MBUTTONUP:
                return true;
        }

        return false;
    }
}