【问题标题】:C# WPF - Long Operation On Button ClickC# WPF - 按钮单击的长时间操作
【发布时间】:2015-10-08 20:13:51
【问题描述】:

我有Button,它在点击时调用另一个方法。 另一种方法执行可能需要很长时间的操作... 所以我想创建一个Label,它出现在操作开始并告诉用户等待,操作完成后,Label会消失。唯一的问题是,因为Button 是一个UI 元素(这就是我认为的原因),所以在Button 点击内更改Label 的调用只有在Button 点击之后才会激活完成...(所以基本上Label 在点击之前是不可见的,并且在点击期间无法更改,所以它保持这种状态)。

这是我的代码:

private void SearchButtonActions()
{
        UI.InvokeA(() => lstFiles.ItemsSource = FDItems);
        bool SearchAndListing = false;
        //UI.InvokeA(() => lblWait.Height = double.NaN);
        //UI.InvokeA(() => lblWait.Visibility = Visibility.Visible);
        //UI.InvokeA(() => lblWait.Content = "Search Started...");
        int index = cbTypes.SelectedIndex;
        string selecteditem = cbSearchOption.SelectedItem.ToString();
        SearchAndListing = FD.Search(index, selecteditem);
        FDItems = new ObservableCollection<Item>(FD.Items);
        //UI.InvokeA(() => lblWait.Height = 0);
        //UI.InvokeA(() => lblWait.Visibility = Visibility.Hidden);
        //UI.InvokeA(() => lblWait.Content = "Search Ended.");
        if (SearchAndListing)
        {
            UI.InvokeA(() => lstFiles.ItemsSource = FDItems);
            UI.InvokeA(() => lblCount.Content = string.Format("Items: {0}", FDItems.Count));
        }
}

我说的是改变lblWait的方法... 顺便说一句:UI.Invoke - 是Dispatcher.Current.InvokeAsync(Action) 的快捷方式

我尝试过使用TasksBackGroundWorker,并将UI.Invoke 更改为Invokesynchronically 而不是asynchronically),但都没有成功...

有人可以帮忙吗?

【问题讨论】:

  • 使用 task.run 和进度条

标签: c# .net wpf user-interface asynchronous


【解决方案1】:
  1. 单击按钮后立即显示“请稍候标签”。
  2. 执行你的长操作:FD.Search(index, selecteditem); in async Task.Run()
  3. 长操作完成后使用ContinueWith()隐藏标签。

    private void SearchButtonActions() {
        lblWait.Visibility = Visibility.Visible;
        Task.Run(() => {
            return FD.Search(index, selecteditem);
        }).ContinueWith((Task<bool> task) => {
            Dispatcher.Invoke(() => { 
              FDItems = new ObservableCollection<Item>(FD.Items));
              lblWait.Visibility = Visibility.Hidden;
            });
        });
    }
    

如果您需要 ContinueWith() 块中某处的 FD.Search() 的结果,请使用 task.Result 检索它。

【讨论】:

  • “System.InvalidOperationException”类型的异常发生在 WindowsBase.dll 中,但未在用户代码中处理附加信息:调用线程无法访问此对象,因为不同的线程拥有它。
  • 该异常只是意味着您必须使用调度程序来调用引发异常的方法。 UI 线程拥有所有 WPF 控件。任务在不同的线程上运行。
  • 我修复了这个异常,但我遇到了另一个问题......我认为由于某种原因,第二个甚至第一个任务没有被执行,因为标签显示但随后它卡住了......甚至如果在操作的情况下它非常快......(它应该在操作完成后停留)......
  • @David47 嗯,好的。有什么办法可以让我看看你的项目吗?我认为这可能是一个简单的问题,我只是没有了解所有上下文。
  • 你有Skype吗?我们可以在那里进行私人聊天,我可以向您发送您需要的任何内容...(加我 dudushnaider)
【解决方案2】:

我之前遇到过这个问题,直到现在才考虑过。

您的论点是正确的,UI 线程当前正在运行 Click 方法,因此它很忙,不会运行其他任何东西。

即使在使用调度程序时,它仍在执行 click 方法,因此 ui 线程会弹出下一个委托以仅在完成后运行。

所以这行不通:

   lbl.Visibility = Visibility.Visible;
   Thread.Sleep(3000);

这是行不通的:

    Dispatcher.Invoke(() => lbl.Visibility = Visibility.Visible);
    Dispatcher.Invoke(() => Thread.Sleep(3000));

可行的是从后台线程调度 ui 操作。

XAML:

    <StackPanel>   
       <Button Click="Button_Click" Content="Click"/>
       <Label x:Name="lbl" Content="Label" Visibility="Hidden" Foreground="Red" FontSize="22" HorizontalAlignment="Center"/>
     </StackPanel>

CS:

    private async void Button_Click(object sender, RoutedEventArgs e)
    {
        await Dispatcher.InvokeAsync(() => 
        {
            Debug.WriteLine("Visibility");
            lbl.Visibility = Visibility.Visible;                
        });

        await Task.Run(() =>
        {
            return Dispatcher.InvokeAsync(() => Thread.Sleep(3000));
        });            
    }

仅供参考:您可能想知道为什么我不使用调度程序将 Visibility 值传播到 UI 线程。这是因为每个 DispatcherObject(我们从 DispatcherObject 派生的 DependencyObject)都将执行传播到它关联的 Dispatcher。

【讨论】:

  • 我已尝试按照您在此处告诉我的方式对其进行修改...但效果不佳...如何向您展示新代码? (这里说太长了……)
  • 你能在谷歌文档上与我分享吗? eranotz50@gmail.com 明天会看到,晚安
  • 我会将文档发送到您的邮箱
  • 如果有其他人想看这里的链接:
猜你喜欢
  • 2011-11-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-07-23
相关资源
最近更新 更多