Very simple question but this has been annoying me for a few days. I have tried quite a few different things to get this working but its starting to piss me off.

So basically I am trying to get a simple WPF app to run through a folder, copy some files and update a textbox as it goes. I have setup a new thread for the copying method so the UI thread isn't hung up and I am using dispatcher.invoke to send back to the UI thread for updating. However because my file copying method is in a different class to the textbox it doesn't update the textbox. I have tried passing the textbox to the copying method as I read this would be a solution but this doesn't appear to work. Or I misunderstood what was being suggested (equally as likely).

You can find my code below. BEWARE: I read a few chapters of a C# book and then just jumped in (as I always do) so my code is likely to be messy.

namespace DeleteTempFiles
{
    /// <summary>
    /// Interaction logic for Window1.xaml
    /// </summary>
    public partial class Window1 : Window
    {

        public Window1()
        {
            InitializeComponent();
        }

        // Copy stuff. To allow 'fileBox' to be parsed at thread Start. There must be a nicer way to do this.
        public void copyStuff()
        {
            try
            {
                DeleteStuff accessDeleteStuff = new DeleteStuff();
                accessDeleteStuff.GetAllFiles(fileBox);
            }
            catch (Exception goneWrong)
            {
                MessageBox.Show(goneWrong.Message);
            }

        }
        // GUI Button click starts thread
        public void displayFiles_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                Window1 threadOne = new Window1();
                Thread deletingThread = new Thread(threadOne.copyStuff);
                deletingThread.SetApartmentState(ApartmentState.STA);
                deletingThread.Start();
            }
            catch (Exception goneWrong)
            {
                MessageBox.Show(goneWrong.Message);
            }
        }

    }

    // Worker Class
    public class DeleteStuff
    {
        // Method for looping through directory and copying x amount of files to another directory.
        public void GetAllFiles(TextBox fileBox)
        {

            try
            {
                int count = 1;
                string startDir = "D:/Pictures/";
                string finishDir = "D:/Copies/";
                string[] fileEntries = Directory.GetFiles(startDir);
                foreach (string fileName in fileEntries)
                {
                    // Only copy 10 files
                    if (count <= 10)
                    {
                        string outcome = fileName.Replace(startDir, finishDir);
                        File.Delete(outcome);
                        File.Copy(fileName, outcome);
                        // Try and update UI fileBox with copy file progress.
                        Application.Current.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal, new Action(delegate()
                            {
                                fileBox.Text = fileBox.Text += "File: " + fileName + " Successfully Copied!" + '\n';
                            }
                            ));
                        count++;
                    }
                }
            }
            catch (Exception goneWrong)
            {
                MessageBox.Show(goneWrong.Message);
            }

        }

    }

}

Any help on fixing this to work would be much appreciated.

Thanks for your time.

P.S - The Names relate to what I plan to do with the program if I can get it working. So thats why they don't make any sense ;)

Recommended Answers

All 5 Replies

This isn't the best solution from a design standpoint, but it should get your application working and then you can figure out how to do a bit cleaner design.

Inside your Window1 class add this:

public delegate void FileBoxDelegate(string filename);

void AppendToFileBox(string filename)
{
      fileBox.Text += "File: " + fileName + " Successfully Copied!" + '\n';
}
 
public void UpdateFileBox(string filename)
{
    BeginInvoke(new FileBoxDelegate(AppendToFileBox), filename);
}

After the statement

Window1 threadOne = new Window1();

Add this:

threadOne.UIWindow = this;

In the DeleteStuff class:

Add a property to hold a reference to the Window1 class. (I used a member variable for brevity)

public Window1 m__uiWindow = null;

Then replace the Application.Current.Dispatcher.BeginInvoke stuff with:

m_uiWindow.UpdateFileBox(fileName);

I think that'll do it.

i have a similar situation i'd like to pursue. let's say from the main app a user requests a series of database operations. i'd like to be able to start the process on the main app, and update a separate small modal status/progress window as the operations start and finish. sorta like:
Operation 1 has started...done!
Operation2 has started...done! etc.
and when all operations have completed, put the focus on the progress form and have the user be able to click an OK button to then close the status/progress log form.


help!

thanks in advance

Best way to do that would be to have a "onCompleted" event you have for your thread, and then all threads are done (having counted them up) you know to finalise all work is done. (using the deligates and invoke code previously provided)

thanks. i think i need to understand the subtleties of using threads, which i have yet to use.

in my main app i collect data and then run some database updates. my current way of approaching the progress monitor synchronously without using threads is like this:
1) instantiate the progress window modally from the main app
2) write some text to the progress window from the main app: "starting first query..."
3) run my first query in the main app, get a return code
4) write some text to the progress window from the main app: "first query successful..."
5) write some text to the progress window from the main app: "starting second query..."
6) run my second query in the main app, get a return code
7) write some text to the progress window from the main app: "second query successful..."
etc.

finally) set the focus to the progress window's OK button for the user to click

problem is, once the progress form is shown, i lose control and can't send the updates.

i don't need to be doing anything else in the main app while this process is taking place, i just need to be able to update the progress window as i do each step, and then leave the user at the progress window so they can digest the running log, and then close the window.

thanks again.

OK, well. a thread is the right answer, keeping processing and your UI apart is a wise move as any major delay for whatever reason in your processing will mess up the UI if you dont get it right.

Make a simple app.
Have an app that you enter the number of threads you want to run, and have it do something along the lines of:

wait a random amount between 0 and 2 seconds
start at 1 and through to say 50 output to a label on the form its current value

so, then when you click a button after entering how many threads you want, you create the labels, pass the label to the thread, and now you can update it as it goes..

Now apply it with the processing required in your app

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.