0

Ok, well I have been at it for a while now and I decided to just use threads. I am making a syntax highlighter but I keep getting terrible performance with the file sizes that it will usually be used for. So I made two forms, the first shows the file in plain text and has a button that says "openincolor" when you click that I start a new thread as such

    private void button1_Click(object sender, EventArgs e)
    {
        ColoringThread colorer = new ColoringThread(this.m_bruteView.Text);
        Thread theThread = new Thread(new ThreadStart(colorer.OpenColorWindow));
        theThread.Start();
    }

    public class ColoringThread
    {
        string text;
        public ColoringThread(string initText)
        {
            text = initText;
        }
        public void OpenColorWindow()
        {
            Form2 form2 = new Form2(text);
            form2.ShowDialog();
        }
    };

I want this form to send back a message each time it has complete say x lines of coloring. Then I will take that and figure out the progress and display it to the user.

How might I go about sending a message, or event(...? can I do that) to my first form to let it know of the others progress?

AnotherUser
  • 1,311
  • 1
  • 14
  • 35
  • You cannot simply run ShowDialog on another thread. Forget the idea that a Window 'runs on a thread'. – H H Aug 17 '12 at 18:40
  • @HenkHolterman That is not strictly true. You can do it with some (large) restrictions. Here is an [example](http://www.codeproject.com/Articles/5454/A-Pretty-Good-Splash-Screen-in-C) of a splash screen being run on a separate thread. – AngryHacker Aug 17 '12 at 18:42
  • I said 'not simply'. And it's never a good idea, not even for a Splash. – H H Aug 17 '12 at 18:44
  • I did simply do it though. I am not sure whats going on in the background though as you point out. Why is it not good as I did it? – AnotherUser Aug 17 '12 at 18:51
  • @HenkHolterman Why is it not a good idea. Splash screen is implemented like that in my WinForms app and it works perfectly fine. – AngryHacker Aug 17 '12 at 18:51
  • @user1596244 It might work for you once, but eventually you'll get a cross-thread violation. – AngryHacker Aug 17 '12 at 18:52
  • What type of violation. Why is it bad... – AnotherUser Aug 17 '12 at 19:00
  • @AngryHacker - it'll work but it's overkill. You can effectively show a Splash by using the low prio of WinForms.Timer. – H H Aug 17 '12 at 19:04
  • @user1596244 - You're on the wrong path. Everything related to the GUI, every update to a control, must run on the single main thread. You'll have to split off the non-GUI part and run that in a Backgroundworker. – H H Aug 17 '12 at 19:06
  • So basically I have a thread inside a thread when I do this. (thread(thread)) and I want them side by side (thread)(thread)? So do I need to go back up to where form1 is started, and start the form2 thread in there somehow? – AnotherUser Aug 17 '12 at 19:10
  • You don't have a 'thread in a thread'. And yes, showing a Form is about calling Show or ShowDIalog, not about threads at all. – H H Aug 17 '12 at 19:23
  • I have started another question for this... http://stackoverflow.com/questions/12011997/proper-way-to-start-a-thread-in-a-winform – AnotherUser Aug 17 '12 at 19:27

4 Answers4

3

One very simple way to do this is with BackgroundWorker. It already provides an event to report progress.

Eric J.
  • 147,927
  • 63
  • 340
  • 553
  • I was looking here http://www.switchonthecode.com/tutorials/csharp-tutorial-using-the-backgroundworker-class at the second example. I like this idea, but my Form that I run has a member (the richtb) which does the work on itself. Form1-> Run Form2 in thread -> Tell rtb to Process -> when thats done display – AnotherUser Aug 17 '12 at 19:07
  • Are you running Form1 and Form2 on separate threads? I would not suggest that pattern. – Eric J. Aug 17 '12 at 19:34
2

How about something like this? This adds an event to the ColoringThread class which is subscribed to by the calling class.

private void button1_Click(object sender, EventArgs e) {
    ColoringThread colorer = new ColoringThread(this.m_bruteView.Text);
    colorer.HighlightProgressChanged += UpdateProgress;
    Thread theThread = new Thread(new ThreadStart(colorer.OpenColorWindow));
    theThread.Start();
}

private void UpdateProgress(int linesComplete) {
    // update progress bar here
}

public class ColoringThread
{
    string text;

    public delegate void HighlightEventHandler(int linesComplete);
    public event HighlightEventHandler HighlightProgressChanged;

    public ColoringThread(string initText) {
        text = initText;
    }

    public void OpenColorWindow() {
        Form2 form2 = new Form2(text);
        form2.ShowDialog();

        int linesColored = 0;
        foreach (String line in text.Split(Environment.NewLine)) {
            // colorize line here

            // raise event
            if (HighlightProgressChanged != null)
                HighlightProgressChanged(++linesColored);
        }
    }
};
Paccc
  • 1,211
  • 13
  • 15
  • This is odd, my actual form does the coloring. When Form2 calls InitializeComponets (after it is called with showdialog in form1), I have the inputText processed before the form shows itself. How might I go about doing this how I have my classes setup? – AnotherUser Aug 17 '12 at 19:03
  • You should move the raise event part to wherever you do the actual syntax highlighting, and move the event declaration and delegate declaration to the Form2 – Paccc Aug 17 '12 at 19:28
1

You can pass an object as argument to the Thread.Start and share your data between the current thread and the initiating thread.


Here is a good example: How to share data between different threads In C# using AOP?


Or you can use BackgroundWorker which has ReportProgress

Community
  • 1
  • 1
TrizZz
  • 1,200
  • 5
  • 15
1

What you need is System.Windows.Threading.Dispatcher's BeginInvoke method. You can't directly modify a WPF object from your background thread, however you can dispatch a delegate to do that.

In your derived Window class object you have the Property Dispatcher, so you use it as follows:

Dispatcher.BeginInvoke(
  DispatcherPriority.Normal,
  (status) => { StatusTextBox.Text = status },
  thestatus
);

I'm sorry that I can't test that currently and I don't have the project here, where I did that. But I'm sure it will work, good luck ;)

Update: Oops, you're using Form's... I've written about WPF, sorry.

metadings
  • 3,798
  • 2
  • 28
  • 37
  • 1
    Your answer is valid if you replace `Dispatcher.BeginInvoke` with `Control.BeginInvoke` (specifically `StatusTextBox.BeginInvoke` in this case). That's the Windows Forms version of the Dispatcher code. If the code might be called from the UI thread, you can add a check to `Control.InvokeRequired` and then just execute your code (rather than "dispatch" it) if it returns false. For more info, see http://msdn.microsoft.com/en-us/library/0b1bf3y3 – Jon Senchyna Aug 17 '12 at 19:06