2009-11-08 37 views
5

我正在使用标准.Net数据绑定从SQL Server类型化的DataSet编写简单的数据UI。在非UI线程上运行模态对话框

我有一个重新加载按钮,在所有DataAdapter上调用Fill从数据库中获取新数据(以防其他用户更改数据)。

这需要一段时间,在此期间UI被冻结。它必须在UI线程上运行,或者数据绑定事件处理程序抛出跨线程异常。

我想在UI线程连接到数据库时在后台线程上显示模式“请稍候”对话框(以便可以动画化)。

如何在非UI线程上显示模态对话框?


编辑:我知道,最好的做法是在后台运行的操作,但我不能这样做,因为数据绑定事件。

回答

8

你应该做相反的事。在后台线程上运行长时间运行的进程,并让UI线程自由地响应用户操作。

如果您想在处理任何用户操作时阻止任何用户操作,那么您有许多选项,包括模态对话框。一旦后台线程处理完,你可以通知主线程患得患失

+0

正如我在问题中解释的那样,我不能。我很清楚这是最佳做法。 – SLaks 2009-11-08 19:52:40

+0

我想你还是可以的。这只是意味着后台线程不应该直接更新数据。检索它们,将它们打包并委托给主线程来更新UI – mfeingold 2009-11-08 20:00:27

+0

我理解你的困境,但我仍然认为我的建议比替代方案更好(更少麻烦) – mfeingold 2009-11-08 20:02:27

0
using(var frmDialog = new MyPleasWaitDialog()) { 
    // data loading is started after the form is shown 
    frmDialog.Load += (_sender, _e) { 
     // load data in separate thread 
     ThreadPool.QueueWorkItem((_state)=> { 
      myAdapter.Fill(myDataSet); 
      // refresh UI components in correct (UI) thread 
      frmDialog.Invoke((Action)myDataControl.Refresh); 
      // close dialog 
      frmDialog.Invoke((Action)frmDialog.Close()); 
     } 
    } 

    // shows dialog 
    frmDialog.ShowDialog(this); 
} 
+0

您需要在不同数据集中加载数据,该数据集未绑定到数据网格,然后将数据网格重新绑定到新数据集。 和/或尝试在数据集/数据表上使用“BeginLoadData()”和“EndLoadData”。 – TcKs 2009-11-08 19:57:45

+1

正如我试图在问题中解释的,当在后台线程上调用myAdapter.Fill时,会在网格的数据绑定事件处理程序中引发InvalidOperationException。在表上调用'BeginLoadData'没有帮助。 – SLaks 2009-11-08 19:58:40

+1

因此,您需要填充另一个数据集,然后将datagrid重新绑定到“新”(已填充)的数据集实例。 – TcKs 2009-11-08 20:02:46

2

在数据绑定事件需要从用户界面分离,很可能使用某种类型的数据传输对象的运行代码。

然后,您可以在单独的线程或BackgroundWorker中运行查询操作,并使UI线程保持原样。

编辑:真的快速的方法来解决这个问题是让事件使用InvokeRequired.Invoke在自己的委托运行。这将给方法UI上下文。我的同事这样做是因为它的风格过时了,它让我感到无法接受,因为这样做并不是一个好主意......但是如果你想要一个快速解决方案,这将起作用。 (我不在工作,所以我没有与我的样品,我会尝试拿出一些东西。)

编辑2:我不知道你要求什么是可能的。我做了一个示例应用程序,它在另一个线程中创建了一个模态对话框,并最终成为无模式对话框。您可以使用其他一些控件或一组控件来指示进度更改,而不是使用模式对话框,而最可能直接在同一个表单上进行更改?

+0

有没有简单的方法来做到这一点,仍然使用标准的数据绑定更新事件?我正在尽快完成这个项目。 – SLaks 2009-11-08 19:57:16

+0

Re。编辑:我不能在DataGrid中放入'Invoke'调用。 – SLaks 2009-11-08 20:03:55

+0

回复:编辑2:你可能是对的。如果这是一个简单的问题,我不会在这里问。至于在表单上放置控件,情况更糟 - 完全不可能在由不同线程拥有的表单上拥有某些控件。 – SLaks 2009-11-08 20:40:59

0

下面是使用BackgroundWorker的做数据的加载和运行一个用户友好的形式展现“负载记录”的例子或类似的...

public void Run() 
    { 
     bgWorkrFillDS = new BackgroundWorker(); 
     bgWorkrFillDS.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgWorkrFillDS_RunWorkerCompleted); 
     bgWorkrFillDS.DoWork += new DoWorkEventHandler(bgWorkrFillDS_DoWork); 
     bgWorkrFillDS.RunWorkerAsync(); 
    } 

    void bgWorkrFillDS_DoWork(object sender, DoWorkEventArgs e) 
    { 
     BackgroundWorker bgWrkrFillDS = (BackgroundWorker)sender as BackgroundWorker; 
     if (bgWrkrFillDS != null) 
     { 
      // Load up the form that shows a 'Loading....' 
      // Here we fill in the DS 
      // someDataSetAdapter.Fill(myDataSet); 
     } 
    } 


    void bgWorkrFillDS_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 
    { 
     // Hide or unload the form when the work is done 
    } 

希望这有助于... 保重, 汤姆。

+1

请阅读问题。在后台线程上调用'someDataSetAdapter.Fill'将会抛出一个InvalidOperationException异常。 – SLaks 2009-11-08 20:14:09

0

我解决了这个问题,通过创建一个新的DataSet,在后台加载,然后在UI线程上调用DataSet.Merge。感谢大家的建议,这导致了这个解决方案。

作为一个额外的奖励,这运行比以前更快(在后台调用Fill,它只能在没有网格打开的情况下工作)。有谁知道为什么?