0

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

我可以设置

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

3 回答 3

1

与往常一样,UI 应该以这样一种方式呈现,即用户了解应用程序正在做什么以及should talk to the user使用 UI 元素。

正如 Adam Houldsworth 建议的那样,我也更喜欢保持button禁用或启用,但我还建议按钮的标题应该向用户传达消息,即新线程启动时正在进行长时间处理......所以标题的button应该立即更改为类似"Processing..Please wait..."(除了被禁用或即使您想保持启用),然后如果您保持启用按钮,只需检查按钮的标题(或 isProcessing bool 标志)click如果它说“正在处理..请等待...”或(isProcessing == true),则返回它的事件。

许多帮助用户上传的网站都会files/images更改Upload按钮的标题以"Uploading..Please wait..."通知用户等待上传完成,另外一些网站还disable设置了上传按钮,这样用户就无法再次点击上传按钮。

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

可能还有其他高级方法,但我们的想法是使其尽可能简单和基本。

查看有关Windows 窗体中的线程的示例,该示例显示在多线程时禁用按钮。

于 2012-06-21T09:53:52.523 回答
0

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

这可以通过 Winforms [No hassles code] 中的 Backgroundworker 组件优雅地实现。只需复制粘贴并按 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;
        }

    }
}
于 2012-06-21T11:24:38.097 回答
0

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

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

创建您的过滤器以使其抑制您不需要的鼠标消息,并在必要时使用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;
    }
}
于 2012-06-21T16:28:52.517 回答