【问题标题】:Make main thread execute code on button press after form.show让主线程在 form.show 后按下按钮时执行代码
【发布时间】:2016-11-02 11:57:40
【问题描述】:

我有一段代码可以进行一些计算,然后调用 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() 在哪里 throw new Exception("这实际上正在执行");

【问题讨论】:

  • 我希望这足够清楚,如果您有任何问题,请随时发表评论。
  • 你应该在另一个线程上工作,而不是 UI 线程。
  • @DanielA.White 我有一个 button.OnClick 事件,我可以将线程附加到该事件,但这似乎使用了 gui 线程。如何将其移至主线程?
  • 您应该创建自己的线程或使用线程池中的一个。
  • @DanielA.White 到底怎么样?我的意思是我的主线程必须是调用 GUI 线程的那个(否则它会引发异常),然后从 GUI 线程我希望主线程再次执行一些代码。

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


【解决方案1】:

如果您将 Thread.Sleep 替换为 System.Windows.Forms.Application.DoEvents(),您的第一个示例可能会起作用。它应该留出时间来绘制 GUI,并且不要完全冻结应用程序。

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

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

【讨论】:

    【解决方案2】:

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

    使用 idling 事件,您将注册它,当它注册时,您的代码(在主线程中)每次不忙于其他事情时都会从 Revit 获得回调。通常每秒几次。

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

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

    Jeremy Tammik 必须在 thebuildingcoder.typepad.com 上发布 20 篇关于无模式对话框/Revit 内容的帖子。

    【讨论】:

    • 你能举例说明如何在外部事件或空闲事件中使用它吗?我在 thebuildingcoder.typepad.com 上看到了这篇文章,但我不确定我是否理解它,因为他的示例代码似乎适用于不同的语言 darenatwork.blogspot.nl/2015/06/…
    【解决方案3】:

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

    【讨论】:

    • 我在您网站上的示例中查看了您的代码(至少我希望我得到了正确的代码)。并设置如下内容: public static void RegisterAPIThreadAndHold(ExternalCommandData commandData) { UIApplication uiapp = commandData.Application; uiapp.Idling += Application_Idle;但是这并没有执行任何想法(我会更新我的问题)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-01
    相关资源
    最近更新 更多