在 form.show 之后让主线程在按下按钮时执行代码
Make main thread execute code on button press after form.show
我有一段代码可以进行一些计算,然后调用 form.show 命令。现在我有一个库(revit api)不允许我在不在主线程中的情况下将变量存储在项目中。
逻辑上的解决方案是让生成的线程使用 producer/consumer 模式调用主线程,代码看起来有点像这样:
form.Show(owner);
while(AppIsRunning){
if(clicked)
commit();
else
Thread.sleep(100);
}
但是,当我执行此操作时,GUI 无法完全加载(黑色背景,按钮分机中没有文本)。
我也尝试过使用唤起方法来做到这一点
private void BtnOK_Click(object sender, System.EventArgs e)
{
Commit();
Invoke(Commit);
}
private void Invoke(Action commit)
{
commit.Invoke();
}
然而,这只是告诉我执行提交函数的不是主线程。
有没有其他方法可以做到这一点,或者我只是犯了一个错误。
需要说明的是,我有一个 form.show(owner) 命令,如果它没有被主线程执行,它会抛出一个错误。我还有一个 commit() 函数,它必须被主线程原谅,否则它会抛出错误。执行必须等到按下按钮。但是主线程轮询 gui 线程进行更改会导致程序挂起。根据我的 google 搜索,也可以做一些涉及外部事件的事情来返回正确的上下文,但是给出的例子是使用 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 覆盖结果执行(ExternalCommandData commandData,引用字符串消息,ElementSet 元素)
Form frm = new OverviewForm(ExternalCommandData commandData);
frm.show()
ThreadManager.RegisterAPIThreadAndHold(commandData);
ThreadManager.Execute(new run_ThrowError())
其中 ThrowError.execute() 是
Throw new Exception("这实际上正在被执行" );
我知道的两种方法是使用外部事件或空闲事件。
对于空闲事件,您将注册它,当它被注册时,您的代码(在主线程中)每次不忙于其他事情时都会从 Revit 获得回调。通常每秒几次。
进入空闲回调后,您就可以创建交易并与模型交互。所以你的回调检查表单的状态并决定是否有事情要做。
外部事件在注册方面的工作方式类似,但您可以请求触发回调。
Jeremy Tammik 在 thebuildingcoder.typepad.com 上必须有 20 个关于无模式对话框/Revit 内容的帖子。
有关此问题的简单解决方案,请参阅 Revit SDK ModelessDialog ModelessForm_ExternalEvent 示例应用程序。它准确地展示了您的要求。
如果您将 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 对象文档和示例。
我有一段代码可以进行一些计算,然后调用 form.show 命令。现在我有一个库(revit api)不允许我在不在主线程中的情况下将变量存储在项目中。
逻辑上的解决方案是让生成的线程使用 producer/consumer 模式调用主线程,代码看起来有点像这样:
form.Show(owner);
while(AppIsRunning){
if(clicked)
commit();
else
Thread.sleep(100);
}
但是,当我执行此操作时,GUI 无法完全加载(黑色背景,按钮分机中没有文本)。
我也尝试过使用唤起方法来做到这一点
private void BtnOK_Click(object sender, System.EventArgs e)
{
Commit();
Invoke(Commit);
}
private void Invoke(Action commit)
{
commit.Invoke();
}
然而,这只是告诉我执行提交函数的不是主线程。 有没有其他方法可以做到这一点,或者我只是犯了一个错误。
需要说明的是,我有一个 form.show(owner) 命令,如果它没有被主线程执行,它会抛出一个错误。我还有一个 commit() 函数,它必须被主线程原谅,否则它会抛出错误。执行必须等到按下按钮。但是主线程轮询 gui 线程进行更改会导致程序挂起。根据我的 google 搜索,也可以做一些涉及外部事件的事情来返回正确的上下文,但是给出的例子是使用 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 覆盖结果执行(ExternalCommandData commandData,引用字符串消息,ElementSet 元素)
Form frm = new OverviewForm(ExternalCommandData commandData);
frm.show()
ThreadManager.RegisterAPIThreadAndHold(commandData);
ThreadManager.Execute(new run_ThrowError())
其中 ThrowError.execute() 是 Throw new Exception("这实际上正在被执行" );
我知道的两种方法是使用外部事件或空闲事件。
对于空闲事件,您将注册它,当它被注册时,您的代码(在主线程中)每次不忙于其他事情时都会从 Revit 获得回调。通常每秒几次。
进入空闲回调后,您就可以创建交易并与模型交互。所以你的回调检查表单的状态并决定是否有事情要做。
外部事件在注册方面的工作方式类似,但您可以请求触发回调。
Jeremy Tammik 在 thebuildingcoder.typepad.com 上必须有 20 个关于无模式对话框/Revit 内容的帖子。
有关此问题的简单解决方案,请参阅 Revit SDK ModelessDialog ModelessForm_ExternalEvent 示例应用程序。它准确地展示了您的要求。
如果您将 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 对象文档和示例。