1

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?

Dayan
  • 7,634
  • 11
  • 49
  • 76
Pipeline
  • 1,029
  • 1
  • 17
  • 45
  • Do you really need events here? Why not just create another form that will display asking the user if they wish to Rename or Ignore? When that form exists then continue to the next one. – Dayan Jul 24 '15 at 17:58
  • I feel like it is not as good of a user experience having forms pop up. This event can happen many times and we want to make it as clean as possible. – Pipeline Jul 24 '15 at 17:59

2 Answers2

2

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);
        }
    }
}
Community
  • 1
  • 1
Dayan
  • 7,634
  • 11
  • 49
  • 76
  • 1
    Swap your `mre.WaitOne();` and `mre.Reset();` lines as they are backwards. You want to block the thread **after** the UI has been enabled and updated. Also, change `Program.form1.Invoke()` to `this.Invoke()`...why use a static variable in Program.cs? – Idle_Mind Jul 24 '15 at 21:00
  • @Idle_Mind Updated answer with the recommended changes, thanks. – Dayan Jul 27 '15 at 18:24
1

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

}
Idle_Mind
  • 38,363
  • 3
  • 29
  • 40