如何忽略WinForms中的用户点击?

时间:2012-06-21 09:41:41

标签: winforms multithreading background-process messages

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

我可以设置

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
}

3 个答案:

答案 0 :(得分:1)

与往常一样,UI应该以这样的方式呈现:用户了解应用程序正在执行的操作以及使用UI元素should talk to the user

正如Adam Houldsworth建议我也希望保持button被禁用或启用但我还建议按钮的标题应该向用户传达这样的信息:新的处理正在进行中线程开始..所以button的标题应立即更改为"Processing..Please wait..."(除了被禁用或甚至你想保持启用),然后如果你保持启用按钮只是检查其click事件上的按钮标题(或isProcessing bool标志),如果它显示“Processing..Please wait ...”或(isProcessing == true)则返回。

许多帮助用户上传files/images的网站会将Upload按钮的标题更改为"Uploading..Please wait...",以通知用户等到上传完成,此外还有一些网站{{1}上传按钮,以便用户无法再次点击上传按钮。

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

可能还有其他先进的方法,但想法是尽可能保持简单和基本。

Threading in Windows Forms上查看此示例,该示例显示在多线程时禁用按钮。

答案 1 :(得分:0)

目前为止所有建议的+1。正如CSharpVJ建议的那样 - My idea was to additionally inform the user by changing the button's caption making the UI design more intuitive

这可以通过Winforms中的Backgroundworker组件优雅地实现[无麻烦代码]。只需复制粘贴和HIT 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;
        }

    }
}

答案 2 :(得分:0)

正如其他答案中所提到的,可能有一个比你要求的更好的解决方案。

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

创建过滤器以禁止显示您不需要的鼠标消息,并在必要时使用Application.AddMessageFilter()应用它。

这些方面的东西(这可能应该编译......):

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