在form.show之后按下按钮,使主线程执行代码

时间:2016-11-02 11:57:40

标签: c# multithreading user-interface producer-consumer revit-api

我有一段代码可以进行一些计算,然后调用form.show命令。现在我有一个库(revit api),它不允许我在项目中存储变量而不在主线程中。

这个逻辑解决方案是让生成的线程使用生产者/消费者模式调用主线程,代码看起来像这样:

form.Show(owner);
while(AppIsRunning){
    if(clicked)
        commit();
    else   
        Thread.sleep(100);
}

然而,当我这样做时,gui没有完全加载(黑色背景,按钮分机中没有文字)。

我也尝试过使用evoke方法

    private void BtnOK_Click(object sender, System.EventArgs e)
    {
        Commit();
        Invoke(Commit);
    }

    private void Invoke(Action commit)
    {
        commit.Invoke();
    }

然而,这只是告诉我它不是执行提交函数的主线程。 还有另一种方法可以做到这一点,还是我只是犯了一个错误。

为了清楚我有一个form.show(owner)命令,如果主线程没有执行它会抛出错误。我还有一个commit()函数,必须由主线程来解释,否则会抛出错误。执行必须等到按下按钮。但是轮询gui线程进行更改的主线程导致程序挂起。根据我的谷歌搜索,也有可能做一些涉及外部事件的事情,以回到正确的上下文,但给出的例子是使用python来调用c#代码,是否有一个很好的方法来引发外部事件重新进入c#中的给定线程?

编辑:基于一些建议我创建了以下代码:

public class ThreadManager
{
    static List<ThreadAble> orders = new List<ThreadAble>();
    public static bool running = false;
    public static void execute(ThreadAble action)
    {
        orders.Add(action);

    }

     static System.Timers.Timer timer;
    public static void RegisterAPIThreadAndHold(ExternalCommandData commandData)
    {

        UIApplication uiapp = commandData.Application;

        uiapp.Idling += Application_Idle;


    }


    private static void Application_Idle(Object o,IdlingEventArgs e)
    {
        if (orders.Count != 0)
        {
            ThreadAble f = orders.First();
            orders.Remove(f);
            f.execute();
        }
    }
}
public interface ThreadAble {
      void execute();        
}

然而,当我使用它时,这似乎并没有实际运行       public override Result Execute(ExternalCommandData commandData,ref string message,ElementSet elements)

   Form frm = new OverviewForm(ExternalCommandData commandData);
   frm.show()
    ThreadManager.RegisterAPIThreadAndHold(commandData);
    ThreadManager.Execute(new run_ThrowError())

ThrowError.execute()所在的位置 抛出新的异常(“这实际上正在执行”);

3 个答案:

答案 0 :(得分:1)

如果要用System.Windows.Forms.Application.DoEvents()替换Thread.Sleep,那么你的第一个例子就可以了。它应该给绘制GUI的时间,并且不要完全冻结应用程序。

form.Show(owner);
while(AppIsRunning){
    if(clicked)
        commit();
    else   
    {
        System.Windows.Forms.Application.DoEvents();
        // Thread.sleep(100);
    }
}

但这并不是实现这一目标的完美解决方案。 更好的方法是在对话框中调用Dispatcher.Invoke命令来执行MainThread操作。 您可以使用ie GalaSoft库 - 请参阅DispatcherHelper对象文档和示例。

答案 1 :(得分:0)

我知道的两种方法是使用外部事件或空转事件。

对于空闲事件,您将对其进行注册,并且在注册时,您的代码(在主线程中)将在每次不忙于其他内容时从Revit获得回调。通常每秒几次。

进入空闲回调后,您就可以创建交易并与模型进行交互。所以你的回调会检查表单的状态并决定是否有事情要做。

外部事件在注册方面的工作方式类似,但您可以请求触发回调。

杰里米·塔姆米克必须在“无模式对话框/ Revit”中的thebuildingcoder.typepad.com上发帖20条。

答案 2 :(得分:0)

有关此问题的简单解决方案,请参阅Revit SDK ModelessDialog ModelessForm_ExternalEvent示例应用程序。它准确地证明了你的要求。