0

在不使用额外线程的情况下,我只想在读取或写入大量数据时显示“正在加载”标签或类似于用户的内容。但是,如果我在调用 IO 方法之前尝试修改任何 UI 元素,应用程序会冻结一段时间,然后在所有工作完成后显示“正在加载”消息。这显然没有帮助。在调用 IO 方法之前,如何确保所有 UI 更改都已应用且可见?

        DataSet ds = STT_Import.ImportExcelToDataSet(filePath);

        bool result = false;

        if (ds != null)
        {
            int cellCount = ds.GetTotalCellCount();

            if (Popup.ShowMessage(string.Format("Your file contains {0} cells. Inserting data will take approximately {1} seconds. Do you want to continue?",
                cellCount, CalculateTime(cellCount)), "Confirm", MessageType.Confirm) == MessageBoxResult.Yes)
            {
                // Tell user the application is working:
                StatusLabel.Content = "Writing to database...";

                // Do actual work after user has been notified:
                result = DB.StoreItems(_currentType, ds);
            }
        }

我试图寻找答案,但找不到任何可以回答我的具体问题的东西,所以如果之前有人问过这个问题,我很抱歉。

4

5 回答 5

1

使用 WPF 时,您可以使用Dispatcher在不同DispatcherPriorities的 UI 线程上对命令进行排队

这将允许您在或队列中的所有内容发生后,在 UI 线程上对长时间运行的进程进行排队。DispatcherPriority.RenderDispatcherPriority.Loaded

例如,您的代码可能如下所示:

// Tell user the application is working:
StatusLabel.Content = "Writing to database...";

// Do actual work after user has been notified:
Dispatcher.BeginInvoke(DispatcherPriority.Input,
    new Action(delegate() { 
        var result = DB.StoreItems(_currentType, ds);     // Do Work
        if (result)
            StatusLabel.Content = "Finished";
        else
            StatusLabel.Content = "An error has occured";
     }));

应该注意的是,它通常被认为是在某些东西运行时锁定应用程序的糟糕设计。

更好的解决方案是在后台线程上运行长时间运行的进程,并在运行时简单地禁用您的应用程序表单。有很多方法可以做到这一点,但我个人更喜欢使用任务并行库,因为它很简单。

例如,您使用后台线程的代码如下所示:

using System.Threading.Tasks;

...

// Tell user the application is working:
StatusLabel.Content = "Writing to database...";
MyWindow.IsEnabled = False;

// Do actual work after user has been notified:
Task.Factory.StartNew(() => DB.StoreItems(_currentType, ds))
    // This runs after background thread is finished executing
    .ContinueWith((e) =>
    {
        var isSuccessful = e.Result;

        if (isSuccessful)
            StatusLabel.Content = "Finished";
        else
            StatusLabel.Content = "An error has occured";

        MyWindow.Enabled = true;
    });
于 2013-02-11T17:51:29.263 回答
0

如果您不希望 UI 具有响应性,我会使用忙碌指示器。
有更漂亮的光标 - 这是一个内部应用程序。

using (new WaitCursor())
{
    // very long task
    Search.ExecuteSearch(enumSrchType.NextPage);
}

public class WaitCursor : IDisposable
{
    private Cursor _previousCursor;

    public WaitCursor()
    {
        _previousCursor = Mouse.OverrideCursor;

        Mouse.OverrideCursor = Cursors.Wait;
    }

    #region IDisposable Members

    public void Dispose()
    {
        Mouse.OverrideCursor = _previousCursor;
    }

    #endregion
}
于 2013-02-11T17:25:51.403 回答
0

您使用的方法不是有效的方法,所以我建议使用异步编程或线程

异步编程:

Visual Studio 2012 引入了一种简化的方法,即异步编程,它利用 .NET Framework 4.5 和 Windows 运行时中的异步支持。编译器完成了开发人员过去常做的艰巨工作,并且您的应用程序保留了类似于同步代码的逻辑结构。因此,您只需付出很少的努力即可获得异步编程的所有优势。支持 .Net framework 4.5
它将节省您实施 System .Threading 的时间,并且对于与您必须等待某些操作的任务相同的任务非常有效

http://msdn.microsoft.com/en-ca/library/vstudio/hh191443.aspx
http://go.microsoft.com/fwlink/?LinkID=261549

或者

穿线:

线程的优点是能够创建使用多个执行线程的应用程序。例如,一个进程可以有一个管理与用户交互的用户界面线程和在用户界面线程等待用户输入时执行其他任务的工作线程。支持 .Net fremework 4.0 或更早版本

http://msdn.microsoft.com/en-us/library/aa645740%28v=vs.71%29.aspx

于 2013-02-11T15:46:16.887 回答
0

您正试图以错误的方式解决问题。您应该在这里做的是在工作线程中运行耗时的任务;这样,您的 UI 将保持响应,并且当前问题将变得没有意义。

有几种方法可以将任务卸载到工作线程;其中最方便的是使用线程池异步编程

于 2013-02-11T15:46:16.970 回答
0

除非您的数据库提供您正在使用的方法的异步版本,否则在不使用额外线程的情况下保持您的 UI 响应是不可能的。如果它确实提供了该方法的异步版本,那么您只需要使用它。(请记住,异步并不意味着它正在使用任何其他线程。完全有可能创建一个从不使用额外线程的异步方法,而这正是大多数网络 IO 方法所做的。)如何去做的细节这取决于您使用的数据库框架的类型以及您使用它的方式。

如果您的数据库框架不提供异步方法,那么保持 UI 响应的唯一方法是在非 UI 线程中执行长时间运行的操作。

于 2013-02-11T15:56:50.087 回答