Pipeline
Pipeline

Reputation: 1059

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

This is my first time trying to implement a GUI using Windows Forms.

I have a question specific to my project but I will instead use another example that highlights my frustration:

Goal: A program that loop through a file directory and prompt users to rename its folders or not.

So say we have a button:

Start Service

Once the user hits this button the program does all its magic of looping through a directory. So say we have a directory:

A
  - 1
  - 2
B
  -1

So say we only are looping through the top level directory the first folder we come up to in our loop is 'A'.

What I want is the flow of execution to halt here.

There are two more buttons above the 'Start Service button':

Rename
Ignore

I want the execution to halt until one of those two buttons are selected and then certain things happen depending on what the user selects.

I am having trouble with the 'halting' part of this. The last thing I tried:

ManualResetEvent mre = new ManualResetEvent(false);

In start service:

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

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

   }

In the function for the Rename Button Click Event:

   //do some things
   mre.Set();

But my form freezes up and it is not working properly. I am sure that this is possible and I just cant seem to figure it out. Can anyone point me in the right direction?

Upvotes: 1

Views: 1329

Answers (2)

Dayan
Dayan

Reputation: 8031

You need to create a new thread to put the logic in, right now your UI is blocking because you are doing this in your main thread.

So the process is:

  • Create ManualResetEvent
  • Create method to hold the logic that will iterate, read directory structure and allow modifications.
  • Run a new thread for that method you just created.
  • Establish communication with your main thread via Invoke and a delegate (there are multiple ways to do this)

Sample code to get you started: Pause/Resume thread whith AutoResetEvent

Full working example

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

Upvotes: 2

Idle_Mind
Idle_Mind

Reputation: 39132

Here's another example:

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

}

Upvotes: 1

Related Questions