Windows 表单 - 等待按钮点击 return 执行 (Application.DoEvents()?)

Windows Forms - Waiting for a button click to return to execution (Application.DoEvents()?)

这是我第一次尝试使用 Windows 表单实现 GUI。

我有一个特定于我的项目的问题,但我将使用另一个例子来强调我的挫败感:

目标: 循环遍历文件目录并提示用户是否重命名其文件夹的程序。

假设我们有一个按钮:

Start Service

一旦用户点击此按钮,程序就会执行循环访问目录的所有魔法。假设我们有一个目录:

A
  - 1
  - 2
B
  -1

假设我们只循环遍历顶级目录,我们在循环中遇到的第一个文件夹是 'A'。

我想要的是执行流程在这里停止。

'Start Service button'上方还有两个按钮:

Rename
Ignore

我希望在选择这两个按钮之一之前停止执行,然后根据用户的选择发生某些事情。

我在处理其中的 'halting' 部分时遇到了问题。我尝试的最后一件事:

ManualResetEvent mre = new ManualResetEvent(false);

正在启动服务:

   for (... some loop that goes through the directory){ 

        var x = somethign[1]  // "A";
        mre.WaitOne();

   }

在重命名按钮单击事件的函数中:

   //do some things
   mre.Set();

但是我的表单冻结并且无法正常工作。我确信这是可能的,但我似乎无法弄清楚。谁能指出我正确的方向?

您需要创建一个新线程来放入逻辑,现在您的 UI 正在阻塞,因为您正在主线程中执行此操作。

所以过程是:

  • 创建 ManualResetEvent
  • 创建方法来保存将迭代、读取目录结构并允许修改的逻辑。
  • 运行 为您刚刚创建的方法创建一个新线程。
  • 通过调用和委托建立与主线程的通信(有多种方法可以做到这一点)

入门示例代码: Pause/Resume thread whith AutoResetEvent

完整的工作示例

Form1.cs

using System;
using System.IO;
using System.Threading;
using System.Windows.Forms;

namespace ThreadLockExample
{

    public partial class Form1 : Form
    {
        public static ManualResetEvent mre = new ManualResetEvent(true);

        public Form1()
        {
            InitializeComponent();
        }

        private void btnStartService_Click(object sender, EventArgs e)
        {
            btnRename.Enabled = false;
            btnIgnore.Enabled = false;

            Thread thread = new Thread(CheckFiles);
            thread.Start();
        }

        public void CheckFiles()
        {
            var files = Directory.GetFiles("c:\"); //Demo Purposes...

            foreach (var file in files)
            {
                Thread.Sleep(500);

                mre.Reset();

                //Since we are not in our main thread, we need to communicate
                //with the static form variable via an invoke on that Form object.
                this.Invoke((Action) delegate
                {
                    Program.form1.lstDirectories.Items.Add(file); 
                    Program.form1.lstDirectories.Refresh();
                    Program.form1.btnRename.Enabled = true;
                    Program.form1.btnIgnore.Enabled = true;
                });

                mre.WaitOne();
            }
        }

        private void btnRename_Click(object sender, EventArgs e)
        {
            //Do whatever you need to do here, then resume thread.
            mre.Set();
        }

        private void btnIgnore_Click(object sender, EventArgs e)
        {
            //Do whatever you need to do here, then resume thread.
            mre.Set();
        }


    }
}

Program.cs

using System;
using System.Windows.Forms;

namespace ThreadLockExample
{
    static class Program
    {
        public static Form1 form1 = new Form1();

        [MTAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.Run(form1);
        }
    }
}

这是另一个例子:

public partial class Form1 : Form
{

    private bool Rename = false;
    private string RenameTo = "";
    private BackgroundWorker bgw;
    private ManualResetEvent mre = new ManualResetEvent(false);

    public Form1()
    {
        InitializeComponent();

        bgw = new BackgroundWorker();
        bgw.WorkerReportsProgress = true;
        bgw.DoWork += Bgw_DoWork;
        bgw.ProgressChanged += Bgw_ProgressChanged;
        bgw.RunWorkerCompleted += Bgw_RunWorkerCompleted;

        this.Load += Form1_Load;
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        btnIgnore.Enabled = false;
        btnRename.Enabled = false;
    }

    private void btnStart_Click(object sender, EventArgs e)
    {
        FolderBrowserDialog fbd = new FolderBrowserDialog();
        if (fbd.ShowDialog() == DialogResult.OK)
        {
            btnStart.Enabled = false;
            progressBar1.Value = 0;
            progressBar1.Enabled = true;
            bgw.RunWorkerAsync(fbd.SelectedPath);
        }
    }

    private void Bgw_DoWork(object sender, DoWorkEventArgs e)
    {
        string folder = (string)e.Argument;
        DirectoryInfo di = new DirectoryInfo(folder);
        DirectoryInfo[] folders = di.GetDirectories();
        for(int i = 0; i < folders.Length; i++)
        {
            mre.Reset();
            Rename = false;
            DirectoryInfo subFolder = folders[i];
            bgw.ReportProgress((int)((decimal)(i + 1) / (decimal)folders.Length * (decimal)100), subFolder.Name);
            mre.WaitOne();

            if (Rename)
            {
                Console.WriteLine("Rename `" + subFolder.Name + "` to `" + RenameTo + "`");
            }
        }
    }

    private void Bgw_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        progressBar1.Value = e.ProgressPercentage;
        lblFolder.Text = (string)e.UserState;
        btnIgnore.Enabled = true;
        btnRename.Enabled = true;
    }

    private void btnIgnore_Click(object sender, EventArgs e)
    {
        btnIgnore.Enabled = false;
        btnRename.Enabled = false;
        mre.Set();
    }

    private void btnRename_Click(object sender, EventArgs e)
    {
        string newName = txtFolder.Text.Trim();
        if (newName.Length > 0)
        {
            Rename = true;
            RenameTo = newName;
            btnIgnore.Enabled = false;
            btnRename.Enabled = false;
            mre.Set();
        }
        else
        {
            MessageBox.Show("Please enter a New Folder Name first!");
        }
    }

    private void Bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        MessageBox.Show("Done!");
        btnStart.Enabled = true;
        btnIgnore.Enabled = false;
        btnRename.Enabled = false;
        progressBar1.Enabled = false;
    }

}