以最佳方式使用Control.Invoke

时间:2014-01-01 11:05:01

标签: c# windows forms controls invoke

我已经使用visual studio开始了一个典型的Windows窗体项目(c#)。我正在使用BackgroundWorker来填充TreeView控件并显示用户的当前进度。我必须使用Control.Invoke方法来访问我的TreeView控件的方法(如TreeView.Nodes.Add(string ...))。我有两个问题。

是否可以“自动”获取对调用委托方法的对象的引用?例如,当我调用myTree.Invoke(tbu,new object [] {m​​yTree})时,我发送一个myTree对象作为该方法的参数。它是唯一可能的方式,还是我可以像EventHandlers那样做(比如“对象发送者”参数)?

最佳实践是什么:将用于委托的类方法声明为静态(本代码中的TreeBU),或者如下所示 - 为MainForm对象声明一个静态公共变量,然后在初始化委托时使用它object(TreeStart tbu = Program.thisForm.TreeBU)?

对不起我的c#和英文,并提前致谢!

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();
        }
    }
}

1 个答案:

答案 0 :(得分:0)

您通常通过直接传递一个函数(必须与代理签名匹配)来分配一个委托!:

MyCrossThreadDelegateInstance += invokeMe;

new MyCrossThreadDelegate(invokeMe);

检查一下: 您在不同的线程上,并希望使用您的invokeMe()方法更新TreeControl。

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

由于MyTree.BeginUpdate()的调用来自不同的线程,因此抛出了crossthread异常。 为了防止这种情况,我们修改了invokeMe()方法以避免抛出异常:

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

在调用u之前检查是否需要调用 - 当你尝试从不同的线程访问控件时,就是创建控件的控件。这样它就会尝试通过冒泡你的线程树来找到拥有并创建控件的线程。 如果Control.InvokeRequired返回true,则从下一个线程再次调用相同的方法(由委托传递)。重复此过程,直到找到拥有的线程。现在Control.InvokeRequired返回false,你的ELSE块在正确的线程上执行,而不会抛出一个crossthread异常。 有关详细信息,请参阅MSDN Control.Invoke

除非您希望您的委托在全局范围内可用,否则无需声明任何静态内容。

编辑:如果您按照原样使用BackgroundWorker,则ProgressChanged事件将完成工作,因为此事件在适当的线程(UI线程)上升。通过调用BackgroundWorker.ReportProgress()成员触发此事件。有关详细信息,请参阅MSDN - BackgroundWorker class