【问题标题】:Update a winform from an Interface callback from another class and thread从另一个类和线程的接口回调更新 winform
【发布时间】:2015-03-19 13:02:29
【问题描述】:

我有一个 winform 和一个不断发送更新的接口回调。我希望能够从回调接口更新 label1.Text。但是,由于接口在单独的线程上运行,我认为我不能直接执行此操作,因此我尝试使用委托并调用。

但是我遇到了一个错误:

在创建窗口句柄之前,不能在控件上调用 Invoke 或 BeginInvoke - at

 form1.Invoke(form1.myDelegate, new Object[] { so.getString() });

这是完整的代码。

     public partial class Form1 : Form
     {

         MyCallBack callback;
         public delegate void UpdateDelegate(string myString);
         public UpdateDelegate myDelegate;

         public Form1()
         {
             InitializeComponent();

             myDelegate = new UpdateDelegate(UpdateDelegateMethod);
             callback = new MyCallBack(this);
             CallBackInterfaceClass.SetCallBack(callback);

            callback.OnUpdate();
        }

         public void UpdateDelegateMethod (String str)
         {
             label1.Text = str;
         }
     }

     class MyTestCallBack : Callback
     {
         public Form1 form1;
         public SomeObject so;

         public MyTestCallBack(Form1 form)
         {
             this.form1 = form;
         }

         public void OnUpdate(SomeObject someobj)
         {
             so = someobj;
             OnUpdate();
         }

         public void OnUpdate()
         {
             form1.Invoke(form1.myDelegate, new Object[] { so.getString() });
         }

     }

两个问题。

  1. 谁能解释我做错了什么?

  2. 这实际上是最好的方法吗?

这是基于 bokibeg 的回复(见下文)的答案,并进行了一些修改以使其正常工作:

public partial class Form1 : Form {
MyTestCallBack _callback;

public Form1()
{
    InitializeComponent();

    _callback = new MyTestCallBack();
    _callback.MyTestCallBackEvent += callback_MyTestCallBackEvent;
    _callback.OnUpdate();
}

private callback_MyTestCallBackEvent(MyTestCallBackEventArgs e)
{
     if (InvokeRequired)
     {
        Invoke(new Action(() =>
        {
            callback_MyTestCallBackEvent(sender, e);
        }));
        return;
     }
     label1.Text = e.SomeObject.GetDisplayString();
}

class MyTestCallBackEventArgs : EventArgs 
{
     public SomeObject SomeObj { get; set; } 
}

class MyTestCallBack : Callback 
{
    public event EventHandler<MyTestCallBackEventArgs> MyTestCallBackEvent;
    private SomeObject _so;

protected virtual void OnMyTestCallBackEvent(MyTestCallBackEventArgs e)
{
    if (MyTestCallBackEvent != null)
        MyTestCallBackEvent(this, e);
}

public void OnUpdate(SomeObject someobj)
{
    _so = someobj;
    OnMyTestCallBackEvent(new MyTestCallBackEventArgs { SomeObject = _so });
} }

【问题讨论】:

  • 如果我是你,我不会打电话给表单的代表,我会反其道而行之。 Callback 派生类应该简单地引发一个事件,并且您的表单应该订阅该事件。我看不到 MyTestCallBack 的实例化位置,但如果它可能在 Form1 内,这使得您应该使用事件更加明显。编辑:好的,我看到它在哪里实例化了,是的,我肯定会从我的回调类中触发事件,而不是你在上面的代码中尝试的。

标签: c# multithreading winforms


【解决方案1】:

这是我要做的:

public partial class Form1 : Form
{
    MyTestCallBack _callback;

    public Form1()
    {
        InitializeComponent();

        _callback = new MyTestCallBack();
        _callback.MyTestCallBackEvent += callback_MyTestCallBackEvent;
        _callback.OnUpdate();
    }

    private callback_MyTestCallBackEvent(MyTestCallBackEventArgs e)
    {
        // TODO: Invoke code here with e.g. label1.Text = e.SomeObj.ToString()...
    }
}

class MyTestCallBackEventArgs : EventArgs
{
    public SomeObject SomeObj { get; set; }
}

class MyTestCallBack : Callback
{
    public event EventHandler<MyTestCallBackEventArgs> MyTestCallBackEvent;
    private SomeObject _so;

    public MyTestCallBack()
    {
        //
    }

    protected virtual void OnMyTestCallBackEvent(MyTestCallBackEventArgs e)
    {
        if (MyTestCallBackEvent != null)
            MyTestCallBackEvent(e);
    }

    public void OnUpdate(SomeObject someobj)
    {
        _so = someobj;
        OnMyTestCallBackEvent(new MyTestCallBackEventArgs { SomeObject = _so });
    }
}

它将 GUI 逻辑与线程正在执行的任何操作分开。它触发一个事件,Form 有责任随心所欲地做任何事情。

我不确定这是否可以编译,我将它写在文本板中。如果您有任何问题,请告诉我。

您可能刚刚了解了委托并被它迷住了,这与它使用事件类似,但事件在“后端”逻辑中正确放置 - 表单可能会也可能不会用它。还要注意表单的代码要好得多,它没有那么多样板代码只是为了实现一些后台服务类。

但是请注意,MyTestCallBackEvent 事件可能会在您关闭表单后继续触发,因此请确保在表单关闭或处理时(或者当您觉得表单不再需要它时)取消订阅。

哦,我差点忘了:你得到的最初错误是因为你在不需要时调用了Invoke,而 Form 肯定还没有准备好。阅读此问题以查看how to safely invoke controls

【讨论】:

  • 您好,有几个问题:我在 OnMyTestCallBackEvent 中的 if...MyTestCallBackEvent(this, e) 中添加了“this”,让它编译。但我得到一个“跨线程操作无效:控制'label1'从创建它的线程以外的线程访问。”对于 label1.Text = e.SomeObject.ToString().
  • 我确实明白你的意思,它比委托方法更简洁。
  • if (InvokeRequired) { Invoke(new Action(() => { callback_MyTestCallBackEvent(sender, e); }));返回; } 解决了我将用这个更新我的答案的问题。
  • 非常感谢
  • @azuric 没有问题,// TODO: Invoke code here 是您应该放置 if (InvokeRequired) { Invoke(...) } 代码的位置。无论哪种方式,请确保调用 InvokeRequired 甚至更好使用您可以在我的帖子最后一段的链接中找到的帮助函数。
猜你喜欢
  • 2013-02-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-10-15
  • 2017-01-04
相关资源
最近更新 更多