2

为什么我第一次尝试更改此代码中的按钮文本不起作用,而第三次尝试却有效?

串行端口连接时,我的用户将不得不等待几秒钟。之后,我想提醒他他已经连接(第二次尝试可能会导致问题)。

我想让他知道一切都很好,这样他就不会想“duh”并单击两次。

失败。文本更改永远不会出现。

好的,为什么按钮文本的第三个更改有效,但第一个无效?我不知道第二个是否有效。

 /***********************************************************************
  * Button:  Connect Port                                               *
  ***********************************************************************/

 private void TheConnectPortButton_Click(object sender, EventArgs e) 
 {
     string OldText = TheConnectPortButton.Text;

     TheConnectPortButton.Text = "Busy, Please Wait";  /////// This never happens

     ButtonBoss.ButtonHandler_ConnectPort();

     TheConnectPortButton.Text = OldText;              /////// Not sure if this happens

     if (aUartSemaphoreThatTells.IfWeHaveConnectedToAPort == (int)aValueWhichIndicatesThat.YesWeHaveAGoodPortConnected)
     {
         TheConnectPortButton.Text = "Connected";      /////// This one does happen
     }

 }

aUartSemaphoreThatTells.IfWeHaveConnectedToAPort例程也使用确保他不会第二ButtonBoss次连接,以及其他按钮例程(例如,确保我们在 Tx/Rx 或其他任何东西之前连接)。

我尝试在例程返回后更改代码看起来像这样......

    if (aUartSemaphoreThatTells.IfWeHaveConnectedToAPort == (int)aValueWhichIndicatesThat.YesWeHaveAGoodPortConnected)
    {
        TheConnectPortButton.Text = "Connected";
    }
    else
    {
        TheConnectPortButton.Text = OldText;
    }

...我仍然得到相同的结果。

我的猜测(仅此而已)是线程以某种方式参与了所有这一切,并且串行端口例程通过一些我目前没有正确遵循的卷积来胜过按钮文本更改例程。

问题:在连接内容占用系统之前,我需要做什么才能更改文本?

(如果是这样的话)

问题 2:如果我不能做到这一点,我想我已经阅读了有关“灰色”按钮的文章,或者,我相信我在某个地方看到了我实际上可以让按钮在用户眼前消失,以便他无法再次单击它。欢迎提供示例代码的链接。

4

1 回答 1

2

问题是您因此从同一个事件处理程序执行所有操作,因此按钮没有时间更新(重绘)。您可以调用Application.DoEvents();方法,但这根本不是一个好主意,请阅读Application.DoEvents() 的使用

我认为通常您应该将耗时的任务推送到单独的线程中,从中获取进度报告并更新您的 GUI。有很多方法可以创建“工作”线程并从中获得一些响应。例如,使用BackgroundWorker 类

public partial class Form1 : Form
{
    public Form1() { InitializeComponent(); }

    private void button1_Click(object sender, EventArgs e)
    {
        BackgroundWorker w = new BackgroundWorker();
        w.WorkerReportsProgress = true;
        w.DoWork += new DoWorkEventHandler(w_DoWork);
        w.ProgressChanged += new ProgressChangedEventHandler(w_ProgressChanged);
        w.RunWorkerCompleted += new RunWorkerCompletedEventHandler(w_RunWorkerCompleted);
        w.RunWorkerAsync();
        button1.Text = "Started";
    }

    //may influence GUI, as this event handler is run on the GUI thread
    void w_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        button1.Text = "Job is done";
    }
    //may influence GUI, as this event handler is run on the GUI thread
    void w_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        button1.Text = e.ProgressPercentage.ToString();
    }
    //runs in the worker thread...should do the actual job
    //may influence GUI through `ReportProgress`
    //or through `Invoke` method
    void w_DoWork(object sender, DoWorkEventArgs e)
    {
        BackgroundWorker worker = sender as BackgroundWorker;
        for (int i = 1; i <= 10; i++)
        {
            Thread.Sleep(500);
            worker.ReportProgress(10 * i);
        }
    }
}

或者您可以使用任务类

public partial class Form1 : Form
{
    public Form1() { InitializeComponent(); }

    private void button1_Click(object sender, EventArgs e)
    {
        new Task(DoTask).Start();
    }

    void DoTask()
    {
        for (int i = 1; i <= 10; i++)
        {
            Thread.Sleep(500);
            //here you need to update GUI through `Invoke` method
            //as the GUI may only be influenced from the the thread,
            //where it's created
            this.Invoke(new Action<int>((j) =>
                {
                    button1.Text = j.ToString();
                }), 10 * i);
        }
    }
}
于 2013-01-10T00:56:37.157 回答