C# WPF - 按钮单击的长时间操作

Posted

技术标签:

【中文标题】C# WPF - 按钮单击的长时间操作【英文标题】:C# WPF - Long Operation On Button Click 【发布时间】: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 和进度条 【参考方案1】:
    单击按钮后立即显示“请稍候标签”。 执行你的长操作:FD.Search(index, selecteditem); in async Task.Run()

    长操作完成后使用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 明天会看到,晚安 我会将文档发送到您的邮箱 如果有其他人想看这里的链接: docs.google.com/document/d/…

以上是关于C# WPF - 按钮单击的长时间操作的主要内容,如果未能解决你的问题,请参考以下文章

C# Wpf 如何使用其中的按钮更改 Listviewitem 的高度?

C# WPF 在单击重复操作时出现气球提示问题

C#:WPF MVVM 中的按钮绑定

在 WPF 中单击按钮执行文件操作

C#异步调用,界面假死

WPF中想要让一个按钮单击事件结束后自动再单击这个按钮,也就是让单机事件连续反复执行,应该如何实现?