Question

I have started a typical windows forms project (c#) with visual studio. I'm using a BackgroundWorker to fill up a TreeView control and display current progress for user. I have to use a Control.Invoke method to get access for the my TreeView control's methods (like TreeView.Nodes.Add(string ...) ). I have two questions.

Is it possible to "automatically" get reference to the object which invoke delegate method? For example, when I call myTree.Invoke(tbu, new object[] {myTree}) , I send a myTree object as an argument for the method. Is it the only possible way or I can do it in a someway like EventHandlers do (like an "Object sender" argument)?

And what is the best practice: to declare a class method used for delegate as static (TreeBU in this code), or as I have done below - Declare a static public variable for MainForm object and then use it when initialize a delegate object ( TreeStart tbu = Program.thisForm.TreeBU )?

Sorry for my c# and english, and thanks in advance!

namespace SmartSorting
{
    public delegate void TreeStart(TreeView xmasTree);

    static class Program
    {
        public static MainForm thisForm;

        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            thisForm = new MainForm();
            Application.Run(thisForm);
        }
    }

    public partial class MainForm : Form
    {
        public MainForm()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            treeView1.Nodes.Clear();
            backgroundWorker1.RunWorkerAsync(treeView1);
        }

        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            BackgroundWorker worker1 = (BackgroundWorker) sender;
            e.Result = stage1(worker1, (TreeView)e.Argument);
        }

        private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Error != null) MessageBox.Show(e.Error.Message);
        }

        private bool stage1(BackgroundWorker wrkr, TreeView myTree)
        {
            TreeStart tbu = Program.thisForm.TreeBU;
            myTree.Invoke(tbu, new object[] {myTree});
            return true;
        }

        public void TreeBU (TreeView xmasTree)
        {
            xmasTree.BeginUpdate();
        }
    }
}
Was it helpful?

Solution

You usually assign a delegate by directly passing it a function (which must match the delegate signature!):

MyCrossThreadDelegateInstance += invokeMe;

or

new MyCrossThreadDelegate(invokeMe);

Check this: Youre on different thread and would like to update the TreeControl using your invokeMe() method.

private void invokeMe()
{
    MyTree.BeginUpdate();
}

Due to this call on MyTree.BeginUpdate() is coming from a different thread, crossthread exception is thrown. To prevent this we modify our invokeMe() method to avoid throwing the exception:

private void invokeMe()
{
    if (MyTree.InvokeRequired)
        MyTree.Invoke(new CrossThreadDelegate(invokeMe);
    else
        MyTree.BeginUpDate();
}

Before invoking u check if invoke is required - this is the case when u try to access a control from a different thread then the one the control was created on. This way it tries to find the thread which owns and created the control by bubbling up you thread tree. If Control.InvokeRequired returns true, the same method (passed over by the delegate) is called again from the next thread. This is repeated until the owning thread is found. Now Control.InvokeRequired returns false and your ELSE-block is executed on the proper thread whithout throwing a crossthread exception. For more details see MSDN Control.Invoke

There is no need to declare anything static except you want your delegate to be available in a global scope.

Edit: If you would use the BackgroundWorker like it was meant to be, the ProgressChanged event would do the job since this event is risen on the proper thread (UI thread). This event is fired by calling the BackgroundWorker.ReportProgress() member. See MSDN - BackgroundWorker class for more details

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top