C# 以最佳方式使用Control.Invoke

C# 以最佳方式使用Control.Invoke,c#,windows,forms,controls,invoke,C#,Windows,Forms,Controls,Invoke,我已经用visual studio启动了一个典型的windows窗体项目(c#)。我正在使用BackgroundWorker填充TreeView控件并为用户显示当前进度。我必须使用Control.Invoke方法来访问my TreeView控件的方法(如TreeView.Nodes.Add(string…)。我有两个问题 是否可以“自动”获取对调用委托方法的对象的引用?例如,当我调用myTree.Invoke(tbu,new object[]{myTree})时,我发送一个myTree对象作为该

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

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

最佳实践是什么:将用于委托的类方法声明为static(在这段代码中是TreeBU),或者像我下面所做的那样-为MainForm对象声明一个静态公共变量,然后在初始化委托对象时使用它(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();
        }
    }
}

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

选中此项: 您在不同的线程上,希望使用invokeMe()方法更新TreeControl

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

private void invokeMe()
{
    if (MyTree.InvokeRequired)
        MyTree.Invoke(new CrossThreadDelegate(invokeMe);
    else
        MyTree.BeginUpDate();
}
在调用u之前,请检查是否需要调用—当您尝试从创建控件的线程之外的线程访问控件时就是这种情况。通过这种方式,它试图通过弹出线程树来找到拥有并创建控件的线程。 如果Control.InvokeRequired返回true,则从下一个线程再次调用相同的方法(由委托传递)。重复此操作,直到找到所属线程。现在Control.InvokeRequired返回false,并且在正确的线程上执行ELSE块,而不引发交叉线程异常。 有关更多详细信息,请参阅MSDN

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


编辑:如果您想像预期的那样使用BackgroundWorker,则ProgressChanged事件将执行此操作,因为此事件在适当的线程(UI线程)上发生。此事件通过调用BackgroundWorker.ReportProgress()成员激发。有关更多详细信息,请参见大致指导,如果使用Control.Invoke(),则说明您做得不对。在工作线程中尝试从UI获取数据是不正确的,当用户在代码运行时继续与UI交互时,数据可能会随机更改。只需在启动worker之前获取数据,然后将其作为参数传递给RunWorkerAsync()。现在您不再需要随机更改数据,也不再需要调用。情况有点不同。我试图更改窗体的控件,而不是从中读取数据。即使TreeView已作为RunWorkerAsync()的参数传递,我也无法从工作线程调用它的方法-我得到一个异常。谢谢!我根据您的建议重新编写了代码,现在它在没有静态成员的情况下工作。关于ProgressChanged事件:我使用它更新ProgersBar状态,同时通过调用在工作线程中填充TreeView。
new MyCrossThreadDelegate(invokeMe);
private void invokeMe()
{
    MyTree.BeginUpdate();
}
private void invokeMe()
{
    if (MyTree.InvokeRequired)
        MyTree.Invoke(new CrossThreadDelegate(invokeMe);
    else
        MyTree.BeginUpDate();
}