views:

84

answers:

6

Well, the problem is that I've got a lot of code like this for each event passed to the GUI, how can I shortify this? Macros wont do the work I guess. Is there a more generic way to do something like a 'template' ?

private delegate void DownloadProgressDelegate(object sender, DownloaderProgressArgs e);
void DownloadProgress(object sender, DownloaderProgressArgs e) {
    if (this.InvokeRequired) {
        this.BeginInvoke(new DownloadProgressDelegate(DownloadProgress), new object[] { sender, e });
        return;
    }

    label2.Text = d.speedOutput.ToString();
}

EDIT:

OK, how can I make this using anonymous delegate in BeginInvoke:

this.BeginInvoke(new DownloadProgressDelegate(DownloadProgress), new object[] { sender, e });
A: 

You could try T4... but I don't know if it will fit your case well.

liori
A: 

One humble thought is to create one routine that handles all your BeginInvoke cases based on a request-type enumerate and using a switch statement. Then at least you only have to check InvokeRequired once. Note that you should probably use if/else rather than return to control the flow.

Bruce
A: 

Well, one way could be to put the generic codes in static class, and access it like, for instance:

Utility.DownloadSpeedUpdate(frm, sender, e);
KMan
+1  A: 

I personally prefer to put the actual action in an Action, then check if this requires an invoke -- this also has the benefit of not needing to declare so many delegates for use with your BeginInvokes. In other words,

void DownloadProgress(object sender, DownloaderProgressArgs e) 
{
    Action updateLabel = () => label2.Text = d.speedOutput.ToString();

    if (this.InvokeRequired) 
    {
        this.BeginInvoke(updateLabel);
    }
    else
    {
       updateLabel();
    }
}

void DownloadSpeed(object sender, DownloaderProgressArgs e) {

    Action updateSpeed = () => 
    {
        string speed = "";
        speed = (e.DownloadSpeed / 1024).ToString() + "kb/s";
        label3.Text = speed;
    };

    if (this.InvokeRequired)
    {
        this.BeginInvoke(updateSpeed);
    }
    else
    {
        updateSpeed();
    }
}

This approach lends itself well to use an extension method on Controls that takes an Action and runs it through the check for InvokeRequired.

At a minimum, the extension method should look something like:

public static void MaybeInvoke(this Control c, Action action)
{
   if (c.InvokeRequired)
   {
       this.BeginInvoke(action);
   }
   else
   {
       action();
   }
}

Annoyingly, the non-generic Action wasn't introduced until .NET 3.5, so you would need to modify things a bit in the examples I gave -- probably using MethodInvoker -- if you're using an earlier version.

Mark Rushakoff
A: 

.net has (in one sense) a rather poor UI framework that actively encourages the problem you have, and the mixing of business logic and UI.

You may be able to make some headway with generics, delegates, and base class/static helper methods.

However, ideally, you need to layer a UI manager on top of .net that helps you out with this properly. This would allow you to separate the UI from the commands that it executes (i.e. you would dynamically bind UI events like keypresses, menu choices, button clicks etc to underlying command objects rather than handling the UI events directly). The UI event handlers would simply look up the command that is bound to the event, then call a centralised "execute Command X" method, and this would handle all the marshalling to the UI thread, etc.

As well as cleaning up this whole mess, this allows you to easily add things like key bindings and scripting/automation to your app, and makes the UI infinitely more scalable and maintainable.

It's no coincidence that this is the core command dispatch approach used in WCF - If you're not using WCF then it's unfortunately up to you to implement an equivalent. It takes a little bit of work to implement a basic command dispatch system, but unless your application is trivial, you'll be glad you did it.

Jason Williams
A: 

Here's a sample that will save you a bunch of coding if you have many functions requiring InvokeRequired checks. You should notice a few important things:

  • I use EventHandler<DownloaderProgressArgs> instead of creating new delegates for each function.
  • The GetInvokeRequiredDelegate function wraps the code that is the same for all of these functions.
  • This code could be moved into a static class to let you share this among several forms, but that would require more work and a different structure. As it is here, the function just knows which form you're dealing with because the function exists inside that form.

This is all the code that I set up to test GetInvokeRequiredDelegate<T>():

    public partial class Form1 : Form
    {
        public event EventHandler<DownloaderProgressArgs> OnDownloadProgress;
        public event EventHandler<DownloaderProgressArgs> OnDownloadSpeed;

        public Form1()
        {
            InitializeComponent();

            OnDownloadProgress += GetInvokeRequiredDelegate<DownloaderProgressArgs>(DownloadProgress);
            OnDownloadSpeed += GetInvokeRequiredDelegate<DownloaderProgressArgs>(DownloadSpeed);

            new System.Threading.Thread(Test).Start();
        }

        public void Test()
        {
            OnDownloadProgress(this, new DownloaderProgressArgs() { DownloadSpeed = 1000, speedOutput = 5 });
            OnDownloadSpeed(this, new DownloaderProgressArgs() { DownloadSpeed = 2000, speedOutput = 10 });
        }

        EventHandler<T> GetInvokeRequiredDelegate<T>(Action<object, T> action) where T : EventArgs
        {
            return ((o, e) =>
            {
                if (this.InvokeRequired)
                {
                    this.BeginInvoke(action, new object[] { o, e});
                } else 
                {
                    action(o, e);
                }
            });
        }


        void DownloadProgress(object sender, DownloaderProgressArgs d)
        {
            label2.Text = d.speedOutput.ToString();
        }

        void DownloadSpeed(object sender, DownloaderProgressArgs e)
        {
            string speed = "";
            speed = (e.DownloadSpeed / 1024).ToString() + "kb/s";
            label3.Text = speed;
        }
    }

    public class DownloaderProgressArgs : EventArgs {
        public int DownloadSpeed;
        public int speedOutput;
    }
John Fisher