Sunday, May 1, 2011

How to Invoke if the form is not active?

Till this moment I used this method to invoke:

    public string AddText
    {
        set
        {
            if (listView1.InvokeRequired)
            {
                this.Invoke((MethodInvoker)delegate
                {
                    Textbox.text += value + "\n";
                });
            }
            else
            {
                Textbox.text += value + "\n";
            }
        }
    }

And here is the problem:

    var form = Form.ActiveForm as Form1;
    if (form != null)
        form.AddText = "Test";

I'm writing an analyzer which analyze packets. I can't put the form on focus all the time, because I have to do actions on the application. I'm writing this analyzer in order to get a packet so I can analyze it.

If I touch the application I want to analyze Form.ActiveForm returns null.

Is there a way I can invoke and set the textbox to add text even if the form is not on Top of everything?

From stackoverflow
  • You could possibly use a Unit of Work pattern for this with the OnActivation event of the form.

    Put the 'if active form' check in you AddText method. if the form is not active, put the text into a List for later.

    Then, handle the OnActivation event of the form, and if the list has values push them back thu AddText. Then when the form becomes activated (this happens when the form gets focus) the text will fill.

    Even if the OnActivation part doesn't work, this general patter should do the trick.

  • I think the root of the problem is the tight coupling between your packet analyzer and the main form. A packet analyzer should not have to know anything about the active form, or even that there is any form at all. It makes things hard to maintain because it only works under a very specific circumstance that's already untrue some of the time.

    You could turn the problem around, and raise an event from the class that is analyzing the packets. That gives you much better encapsulation because now the packet analyzer needs to know nothing about the rest of the application. You can either create your own event args or reuse one like System.Diagnostics.DataReceivedEventArgs for example. Then your form can sink the DataReceived event and call its own AddText to invoke back to the main thread, and it will work whether the form is visible or not.

    At least that will work but it is a blocking call so the packet analyzer thread will be stopped until the form fully handles the event and the marshalled call back to the main thread has returned. That would be okay if the paket rate isn't very high but usually you wouldn't want to stop a communication thread to wait for the main thread to update a textbox. Another approach would be to log the text using a StringBuilder inside the packet analyzer, and expose it (the accumulated text, not the StringBuilder) via a thread-safe public property. Or if you need to separate the invidual messages then you could add them to a List for example, and have the thread-safe property return an array of accumulated messages. Then your form could poll the analyzer for new logged data using a Timer at whatever rate is appropriate for your application, and only while the form is visible. It would not update quite as fast but the impact on the analyzer thread would be almost nil and it should run faster overall if it consolidates many messages inside the StringBuilder or List in between updates rather than concatenating on to the Text property with every single packet.

  • If the problem that you can't Invoke then your answer would be to use SynchronizationContext it's always available on the Application.Run thread. So do the following:

    In Form OnLoad save SynchronizationContext.Current to static field anywhere. After that you can easily use Post or Send methods for sync or async invokes in GUI Thread.

    Description here: http://msdn.microsoft.com/en-us/library/system.threading.synchronizationcontext.aspx

    Nice article here: http://www.codeproject.com/KB/cpp/SyncContextTutorial.aspx

  • Split the problem into two parts. Log your text using Trace.TraceInformation() and implement a TraceListener that handles the display aspect.

    This way, you can redirect your log output to a file, or the Windows EventLog, or your UI, or whatever, simply with an entry in a config file. You don't have to pick just one. You can do all of the above if it helps.

    This is such a mundane and frequent problem that the MSDN sample for implementing a TraceListener is exactly what you need.

0 comments:

Post a Comment