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

C# 使主线程在form.show之后按按钮执行代码,c#,multithreading,user-interface,producer-consumer,revit-api,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不会完全加

我有一段代码,它执行一些计算,然后调用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();        
}
ThrowError.execute()的位置
抛出新异常(“这实际上正在执行”)

我知道有两种方法可以做到这一点,一种是外部事件,另一种是怠速事件

使用idling事件,您将对其进行注册,在注册过程中,每次Revit不忙于处理其他事情时,您的代码(在主线程中)都会收到一个来自Revit的回调。通常每秒几次

一旦进入空闲回调,就可以创建事务并与模型交互。因此,回调将检查表单的状态,并决定是否需要执行某些操作

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


Jeremy Tammik必须在buildingcoder.typepad.com上发表20篇关于无模式对话框/Revit stuff的文章。

有关此问题的简单解决方案,请参阅Revit SDK ModelessDialog ModelessForm\u ExternalEvent示例应用程序。它准确地演示了您的要求。

如果您将用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命令来执行主线程操作。
您可以使用GalaSoft库-请参阅DispatcherHelper对象文档和示例。

我希望这足够清楚,如果您有任何问题,请随时发表意见。您应该在其他线程上完成工作,不是UI线程。@DanielA.White我有一个button.OnClick事件,我可以将线程附加到它,但它似乎使用gui线程。如何将其移动到主线程?您应该创建自己的线程或使用线程池中的线程。@DanielA.White具体如何?我的意思是,我的主线程必须是调用GUI线程的线程(否则会引发异常),然后从GUI线程我希望主线程再次执行一些代码。你能给出一个如何在外部事件或空闲事件中使用它的示例吗?我在buildingcoder.typepad.com上看到了这篇文章,但我不确定自己是否理解了,因为他的示例代码似乎是针对另一种语言的,我在您网站的示例中查看了您的代码(至少我希望我得到了正确的代码)。并设置如下内容:publicstaticvoidregisterapterhreadandhold(ExternalCommandData commandData){UIApplication uiapp=commandData.Application;uiapp.Idling+=Application_Idle;}但是这并没有执行任何想法(我将更新我的问题)
form.Show(owner);
while(AppIsRunning){
    if(clicked)
        commit();
    else   
    {
        System.Windows.Forms.Application.DoEvents();
        // Thread.sleep(100);
    }
}