【问题标题】:List box selection with time consuming task that needs to update the UI需要更新 UI 的耗时任务的列表框选择
【发布时间】:2013-10-22 06:59:41
【问题描述】:

我有一个应用程序,当用户为列表框选择一个项目时,它会执行一项耗时的任务。

当用户选择一个节目时,应用程序将从 tvdb 中检索所有节目信息并将其显示在 Ui 中。

如果用户在节目仍在加载时快速更改选择,就会出现此问题。

我想让用户可以改变主意,然后在第一次加载时做出另一个选择,并将该信息显示在 Ui 中。

我创建了一个简单的演示应用程序来显示问题:Demo App。

这就是我尝试做的

列表框选择事件处理程序

private  void lb1_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            string sid = lb1.SelectedItem.ToString();

            try
            {
                LoadSeries(Int32.Parse(sid));
            }
            catch (FormatException)
            {
                MessageBox.Show("Please enter a valid series id");
            }
        }

加载系列

private void LoadSeries(int _seriesId)
{

    Task<TvdbSeries> series = Task.Factory.StartNew(() =>
    {
        TvdbSeries seriesloaded = null;
        try
        {
            seriesloaded = m_tvdbHandler.GetSeries(_seriesId, TvdbLanguage.DefaultLanguage, true, true, true, true);
        }
        catch (TvdbInvalidApiKeyException ex)
        {
            MessageBox.Show(ex.Message);
        }
        catch (TvdbNotAvailableException ex)
        {
            MessageBox.Show(ex.Message);
        }

        return seriesloaded;
    }
     );

    series.ContinueWith((antecedent) =>
    {
        UpdateSeries(series.Result);
    },
        TaskScheduler.FromCurrentSynchronizationContext()
    );

}

如果用户快速更改选择,seriesloaded = m_tvdbHandler.GetSeries(_seriesId, TvdbLanguage.DefaultLanguage, true, true, true, true); 行上的应用程序错误并在调试器中显示此消息“WebClient 不支持并发 I/O 操作。 "

我确实发现这是因为我在最后一个请求完成之前提出了一个新请求,但我无法将代码链接到 m_tvdbHandler.GetSeries 因为它的功能来自我正在使用的库和其他人写的.

这是图书馆tvdblib,我确定问题出在我做事的方式而不是图书馆。

【问题讨论】:

  • 您不能为每个请求创建新的TvdbHandler 吗?

标签: c# wpf c#-5.0


【解决方案1】:

当用户进行选择时,您可以禁用 UI,直到信息完全加载并在底部加载时显示一条消息,请稍候。加载完所有内容后,启用 Ui 并隐藏消息。

【讨论】:

  • 我确实想过这样做,但它不会带来非常好的用户体验
  • 在这种情况下,您可以将用户选择排队并一一更新,或者如果可以停止当前操作,则停止它并启动新的新线程
  • 我该怎么做?
【解决方案2】:

您将此问题发布为 C#5.0 问题,因此您应该尽可能多地使用async/await。

private Task<TvdbSeries> LoadSeriesAsync(int _seriesId)
{

    return Task.Run(() =>
    {
        TvdbSeries seriesloaded = null;
        try
        {
            seriesloaded = m_tvdbHandler.GetSeries(_seriesId, TvdbLanguage.DefaultLanguage, true, true, true, true);
        }
        catch (TvdbInvalidApiKeyException ex)
        {
            MessageBox.Show(ex.Message);
        }
        catch (TvdbNotAvailableException ex)
        {
            MessageBox.Show(ex.Message);
        }

        return seriesloaded;
    }
     );
}

如果有LoadSeriesAsync就更好了。

一种方法是在检索系列时禁用 lb1。

私有异步 void lb1_SelectionChanged(object sender, SelectionChangedEventArgs e) { 字符串 sid = lb1.SelectedItem.ToString();

try
{
    lb1.IsEnabled = false;
    var series = await LoadSeriesAsync(Int32.Parse(sid));

    UpdateSeries(series);
}
catch (FormatException)
{
    MessageBox.Show("Please enter a valid series id");
    lb1.IsEnabled = true;
}

}

【讨论】:

  • 我在尝试这个 lbl.Enabled = false 时出错了; -- 错误 The name 'lbl' does not exist in the current context and Task series = await Task.Run(() => -- The 'await' operator can only be used in async method. 请考虑标记此带有“async”修饰符的方法并将其返回类型更改为“Task>”。
  • 然后我尝试将 LoadSeriesAsync 更改为私有异步任务 LoadSeriesAsync(int _seriesId)。但这给了我一个新错误——错误 1 ​​无法将类型 'TvdbLib.Data.TvdbSeries' 隐式转换为 'System.Threading.Tasks.Task'
  • 这一行仍然出现错误 `Task series = await Task.Run(() =>{` 说 无法将类型 'TvdbLib.Data.TvdbSeries' 隐式转换为 'System .Threading.Tasks.Task' 并且此行出现错误return series; 说 Error 2 由于这是一个异步方法,因此返回表达式必须是“TvdbLib”类型。 Data.TvdbSeries' 而不是 'Task' 我已将我的项目升级到:dropbox.com/s/7qliih9jj421382/TVShowInfo.zip,如果您想看一下 .ps,感谢到目前为止的帮助
  • 对不起。错误的复制+粘贴。立即尝试。
  • 感谢它现在开始工作,我注意到虽然创建了一定数量的任务后,UI 会锁定直到某些任务完成,所以我应该看看排队任务可能是 3一次任务,如果用户选择第 4 个节目,如果任务 3 仍在进行或尚未开始,它应该取消任务 3,并在其位置添加一个新任务。还不知道如何做到这一点,或者即使它是一个好主意,也必须研究。
猜你喜欢
  • 2023-03-17
  • 1970-01-01
  • 1970-01-01
  • 2013-03-11
  • 1970-01-01
  • 2014-04-12
  • 1970-01-01
  • 2010-09-27
  • 2017-03-10
相关资源
最近更新 更多