2010-01-20 59 views
1

我们已经通过.NET Compact Framework应用程序获得了Model-View-Presenter设置。一个标准的CF窗体正在实现视图接口,并传递给演示者的构造函数。主持人通过调用view.Run();告诉窗体显示自己。然后该视图自身执行一个Show(),并且演示者再次接管,加载数据并将其填充到视图中。.NET Compact Framework:如何在运行代码之前确保窗体可见?

问题在于控件返回给演示者之前,视图没有完成显示。由于展示者代码在加载数据时阻塞了几秒钟,其效果是该表单几秒钟内不可见。直到演示者完成数据加载后,表单才变得可见,即使在演示者开始加载数据之前,自身的Show()形式本身也是如此。

在标准的Windows环境中,我可以在窗体上使用.Shown事件......但是紧凑框架没有这个,我找不到等价的东西。

有没有人知道在开始演示者的一些代码之前,我的表单是否完全可见,是否存在偶数,拼写或其他方式?在这一点上,我可以确定表单呼叫主持人,并告诉主持人开始它的数据加载。

仅供参考 - 我们正试图避免多线程,以减少复杂性和资源使用。

回答

1

如果您的关键问题是表单在您的演示者数据加载方法完成之前不会绘制,并且您调用了Form_Load中的this.Show(),请尝试在此之后直接应用Application.DoEvents() .Show()强制/允许表单绘制。

protected void Form_Load(blah blah blah) 
{ 
    this.Show(); 
    Application.DoEvents(); 

    ... data loading methods ... 
} 
+0

as rediculous as this is,Application.DoEvents()静态方法解决了问题,而不必使用任何额外的线程。我知道我不是“做正确的事情“并可能导致UI冻结等,但这对于我们所需要的就足够了。 – 2010-01-20 17:19:04

2

的一般规则是:永远不做阻塞UI线程

在Windows(和Windows CE为好)的UI具有异步特性上。这意味着大多数API调用不一定会立即执行它们应该执行的任何操作。相反,它们生成一系列事件,这些事件放入事件队列中,之后由事件泵检索,实质上,这是一个在UI线程上运行的无限循环,逐个从队列中挑选事件并处理它们。

从上面的结论可以得出结论,如果你在请求某个动作(即在你的情况下显示窗口)之后继续在UI线程上做很长的事情,事件泵将不能继续进行拾取事件(因为你还没有控制它),因此,你的请求的行动无法完成。

一般方法如下:如果您必须执行复杂的数据转换/加载/准备/任何,请在单独的线程上执行它,然后使用Control.BeginInvoke将委托注入UI线程,然后触摸从该委托内部实际的UI控件。

尽管您对多线程带来的“复杂性”非常不理智,但很少有人会害怕。这里有一个小例子来说明这一点:

public void ShowUI() 
{ 
    theForm = new MyForm(); 
    theForm.Show(); 

    // BeginInvoke() will take a new thread from the thread pool 
    // and invoke our delegate on that thread 
    new Action(PrepareData).BeginInvoke(null,null); 
} 

public void PrepareData() 
{ 
    // Prepare your data, do complex computation, etc. 

    // Control.BeginInvoke will put our delegate on the UI event queue 
    // to be retrieved and executed on the UI thread 
    theForm.BeginInvoke(new Action(PutDataInTheForm)); 
} 

public void PutDataInTheForm() 
{ 
    theForm.textBox1.Text = "data is ready!"; 
} 

虽然你可能有其他解决办法玩,一般的想法始终保持不变:如果你做任何事情,在UI线程上长时间,你的用户界面将“冻结”。当你在屏幕上添加新的UI元素时,它甚至不会重绘,因为重画也是一个异步过程。

因此,您必须在单独的线程上完成所有复杂和长期的工作,并且只做简单,小巧,保证在UI线程上运行快速的事情。没有其他选择,真的。

希望这会有所帮助。

+0

我对这是解决方案感到乐观!但BeginInvoke引发NotSupportedException“.NET Compact Framework不支持异步调用委托”。 :( – 2010-01-20 16:05:36

+1

.NET Compact Framework不支持BeginInvoke - 但是,您可以使用ThreadPool.QueueUserWorkItem来执行基本相同的操作。如果要使它看起来更好,请将线程池访问换成扩展方法: public static IAsyncResult BeginInvoke (This function Func function); – 2010-01-20 16:33:03

0

不需要创建另一个线程,如果你不想(虽然几秒钟不得不以某种方式处理)。 您可以使用激活的事件。因为它会在表单被激活时触发,所以你需要一个布尔表单本地来检查表单是否被第一次创建。 另一种选择是在完成表单呈现后立即断开事件处理程序。