WPF更新UI线程实现进度条功能

WPF更新UI线程实现进度条功能

我的写法
<Page x:Class="CableInspectionScreen.ConfigPage"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
      xmlns:local="clr-namespace:CableInspectionScreen.Utils" 
      xmlns:hc="https://handyorg.github.io/handycontrol"
      mc:Ignorable="d"
      d:DesignHeight="1080" d:DesignWidth="1920"
      Title="ConfigPage">
    <Page.Resources>
        <local:BooleanToVisibilityConverter x:Key="BooleanToVisibility"/>
    </Page.Resources>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        
        <!--存图设置-->
        <Border BorderThickness="2" BorderBrush="Black" Margin="10" Height="Auto">
            <StackPanel Orientation="Vertical" Margin="10">
                <StackPanel Orientation="Horizontal" Margin="30,30,0,20">
                    <TextBlock Text="存图设置" FontSize="40" FontWeight="Bold" VerticalAlignment="Center"/>
                    <TextBlock Text="{Binding ShowSaveImageErrorInfo}" VerticalAlignment="Center" Foreground="Red" FontSize="18"
                               Margin="20,0,0,0" Visibility="{Binding IsVisible,Converter={StaticResource BooleanToVisibility}}"/>
                </StackPanel>
                <hc:TextBox hc:InfoElement.TitleWidth="150" FontSize="20" Height="50"
                            hc:InfoElement.Placeholder="第一座桥" Text="{Binding BridgeName}"
                            hc:InfoElement.TitlePlacement="Left" hc:InfoElement.Title="桥梁名称" 
                            hc:InfoElement.Necessary="True" Margin="30,30,30,20"/>
                <hc:TextBox hc:InfoElement.TitleWidth="150" FontSize="20" Height="50"
                            Text="{Binding DownBridgeName}" IsReadOnly="True"
                            hc:InfoElement.TitlePlacement="Left" hc:InfoElement.Title="下位机桥梁名称" 
                            Margin="30,30,30,20" Opacity="0.6"/>
                <hc:TextBox hc:InfoElement.TitleWidth="150" FontSize="20" Height="50"
                            hc:InfoElement.Placeholder="NS01"  Text="{Binding CableName}"
                            hc:InfoElement.TitlePlacement="Left" hc:InfoElement.Title="缆索编号" 
                            hc:InfoElement.Necessary="True" Margin="30,30,30,20"/>
                <hc:TextBox hc:InfoElement.TitleWidth="150" FontSize="20" Height="50"
                            Text="{Binding DownCableName}" IsReadOnly="True"
                            hc:InfoElement.TitlePlacement="Left" hc:InfoElement.Title="下位机缆索编号" 
                            Margin="30,30,30,20" Opacity="0.6"/>
                <StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Margin="30,60,30,20">
                    <Button Content="保存" Command="{Binding BridgePartSaveCommand}" FontSize="18" MinWidth="100" MinHeight="50" Style="{StaticResource ButtonPrimary}" IsEnabled="{Binding IsEnabled}" Margin="0,0,30,0"/>
                    <Button Content="清空" Command="{Binding BridgePartCancleCommand}" FontSize="18" MinWidth="100" MinHeight="50" Style="{StaticResource ButtonDefault}" IsEnabled="{Binding IsEnabled}"/>
                </StackPanel>
            </StackPanel>
        </Border>
        
        <!--相机设置-->
        <Border BorderThickness="2" BorderBrush="Black" Margin="10" Height="Auto" Grid.Column="1">
            <StackPanel Orientation="Vertical" Margin="10">
                <TextBlock Text="图片下载" FontSize="40" FontWeight="Bold"  VerticalAlignment="Center" Margin="30,30,0,20"/>
                <hc:TextBox hc:InfoElement.TitleWidth="150" FontSize="20" Height="50" Text="{Binding RemoteFilePath}"
                    hc:InfoElement.Placeholder="/home/nvidia/Projects/work/Camera/Configs" 
                    hc:InfoElement.TitlePlacement="Left" hc:InfoElement.Title="远程文件路径" 
                    Margin="30,30,30,20"/>
                <hc:TextBox hc:InfoElement.TitleWidth="150" FontSize="20" Height="50" Text="{Binding HostFilePath}"
                    hc:InfoElement.Placeholder="D:\Program Files (x86)\HuaTengVision\Camera\Configs" 
                    hc:InfoElement.TitlePlacement="Left" hc:InfoElement.Title="本地文件路径" 
                    Margin="30,30,30,20"/>
                <hc:TextBox hc:InfoElement.TitleWidth="150" FontSize="20" Height="50" Text="{Binding UserName}"
                    hc:InfoElement.Placeholder="nvidia" 
                    hc:InfoElement.TitlePlacement="Left" hc:InfoElement.Title="用户名" 
                    Margin="30,30,30,20"/>
                <hc:TextBox hc:InfoElement.TitleWidth="150" FontSize="20" Height="50" Text="{Binding PassWord}"
                    hc:InfoElement.Placeholder="nvidia" 
                    hc:InfoElement.TitlePlacement="Left" hc:InfoElement.Title="密码" 
                    Margin="30,30,30,20"/>
                <StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Margin="30,60,30,20">
                    <Button Content="下载" Command="{Binding CameraPartSaveCommand}" FontSize="18" MinWidth="100" MinHeight="50" Style="{StaticResource ButtonPrimary}" IsEnabled="{Binding IsEnabled}" Margin="0,0,30,0"/>
                    <!--<Button Content="释放" Command="{Binding CameraPartCancleCommand}" FontSize="18" MinWidth="100" MinHeight="50" Style="{StaticResource ButtonDefault}" IsEnabled="{Binding IsEnabled}"/>-->
                    <StackPanel>
                        <!--<Button Content="下载耗时" Margin="20" Click="Button_Click"></Button>-->
                        <ProgressBar Margin="50" x:Name="progressBar1"  Width="300" Height="20"  Minimum="0" Maximum="100" Visibility="{Binding PrograssVisibility,Converter={StaticResource BooleanToVisibility}}" Value="{Binding ProgressValue, Mode=OneWay}" />
                    </StackPanel>
                </StackPanel>
            </StackPanel>
        </Border>

        <!--其它设置-->
        <Border BorderThickness="2" BorderBrush="Black" Margin="10" Height="Auto" Grid.Column="2">
            <StackPanel Orientation="Vertical" Margin="10">
                <StackPanel Orientation="Horizontal" >
                    <TextBlock Text="参数设置" FontSize="40" FontWeight="Bold"  VerticalAlignment="Center" Margin="30,30,0,20"/>
                    <TextBlock Text="{Binding ShowSaveParamErrorInfo}" VerticalAlignment="Center" Foreground="Red" FontSize="18"
                               Margin="20,0,0,0" Visibility="{Binding ParamErrorInfoVisible,Converter={StaticResource BooleanToVisibility}}"/>
                </StackPanel>
                <hc:TextBox hc:InfoElement.TitleWidth="150" FontSize="20" Height="50" Text="{Binding FrontTOF}"
                            hc:InfoElement.Placeholder="150" 
                            hc:InfoElement.TitlePlacement="Left" hc:InfoElement.Title="前TOF(mm)" 
                            Margin="30,30,30,20"/>
                <hc:TextBox hc:InfoElement.TitleWidth="150" FontSize="20" Height="50" Text="{Binding DownFrontTOF}"
                            IsReadOnly="True" Opacity="0.6"
                            hc:InfoElement.TitlePlacement="Left" hc:InfoElement.Title="下位机前TOF" 
                            Margin="30,30,30,20"/>  
                <hc:TextBox hc:InfoElement.TitleWidth="150" FontSize="20" Height="50" Text="{Binding BackTOF}"
                            hc:InfoElement.Placeholder="150" 
                            hc:InfoElement.TitlePlacement="Left" hc:InfoElement.Title="后TOF(mm)" 
                            Margin="30,30,30,20"/>
                <hc:TextBox hc:InfoElement.TitleWidth="150" FontSize="20" Height="50" Text="{Binding DownBackTOF}"
                            IsReadOnly="True" Opacity="0.6"
                            hc:InfoElement.TitlePlacement="Left" hc:InfoElement.Title="下位机后TOF" 
                            Margin="30,30,30,20"/>
                <StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Margin="30,60,30,20">
                    <Button Content="保存" Command="{Binding ParamPartSaveCommand}" FontSize="18" MinWidth="100" MinHeight="50" Style="{StaticResource ButtonPrimary}" IsEnabled="{Binding IsEnabled}" Margin="0,0,30,0"/>
                    <Button Content="清空" Command="{Binding ParamPartCancleCommand}" FontSize="18" MinWidth="100" MinHeight="50" Style="{StaticResource ButtonDefault}" IsEnabled="{Binding IsEnabled}"/>
                </StackPanel>
            </StackPanel>
        </Border>
        <!--<Button Content="加载相机" Name="reload" Click="CameraLoad_Click" HorizontalAlignment="Left" Margin="895,739,0,0" VerticalAlignment="Top" Width="92" Height="44"/>
        <Button Content="释放相机" Name="release" Click="CameraRelease_Click" HorizontalAlignment="Left" Margin="1036,739,0,0" VerticalAlignment="Top" Width="92" Height="44"/>-->
        
    </Grid>
</Page>

private void SaveCameraConfig(object obj)
{
    // ... (略)

    using (var client = new SftpClient(selectRobotIp, userName, passWord))
    {
        try
        {
            client.BufferSize = 1024;
            client.Connect();
        } 
        catch (Exception ex)
        {
            Console.WriteLine("连接失败: " + ex.Message);
        }
        try
        {
            var fileSize = client.GetAttributes(remoteFilePath).Size;
            using (var fileStream = File.OpenWrite(localFilePath))
            {
                client.DownloadFile(remoteFilePath, fileStream, downloaded =>
                {
                    // 计算并显示下载进度
                    double progress = (double)downloaded / fileSize * 100;
                    ProgressValue = (int)progress; // 这里更新了进度值
                });
            }
            client.Disconnect();
        }
        catch (Exception ex)
        {
            // ... (略)
        }
    }
}

gpt建议的写法
// 重复下载会将上次覆盖
        private async void SaveCameraConfig(object obj)
        {
            var messageResult = MessageBox.Show("请确保图片已经采集完成并压缩结束, 是否继续?", 
                "图片下载", MessageBoxButton.YesNo, MessageBoxImage.Warning);
            if (messageResult != MessageBoxResult.Yes)
            {
                return;
            }
            SystemModel device = SystemModel.GetGlobalInstance();
            int robotindex = device.SelectedRobotIndex;
            string selectRobotIp = "10.60.2.202";
            string temp;
            if (!Directory.Exists(hostFolderPath))
            {
                temp = "本地文件夹不存在,请检查!";
                Growl.Warning(temp, _token);
                return;
            }
            // 显示进度条
            PrograssVisibility = true;
            ProgressValue = 1;
            // 本地文件完整路径,包括文件名
            string localFilePath = Path.Combine(hostFolderPath, Path.GetFileName(remoteFilePath));
            try
            {
                await Task.Run(() =>
                {
                    using (var client = new SftpClient(selectRobotIp, userName, passWord))
                    {
                        try
                        {
                            client.BufferSize = 1024;
                            client.Connect();
                        }
                        catch (Exception ex)
                        {
                            Console.WriteLine("连接失败: " + ex.Message);
                        }
                        try
                        {
                            var fileSize = client.GetAttributes(remoteFilePath).Size;
                            // 捕获开始时间
                            Stopwatch stopwatch = new Stopwatch();
                            stopwatch.Start();
                            using (var fileStream = File.OpenWrite(localFilePath))
                            {
                                client.DownloadFile(remoteFilePath, fileStream, downloaded =>
                                {
                                    // 计算并显示下载进度
                                    double progress = (double)downloaded / fileSize * 100;
                                    // 在UI线程上更新进度条的值,但是会导致下载卡死
                                    Application.Current.Dispatcher.Invoke(() =>
                                    {
                                        ProgressValue = (int)progress;
                                    });
                                    Console.WriteLine($"Downloaded {downloaded} of {fileSize} bytes ({progress:F2}%)");
                                });
                            }
                            // 捕获结束时间
                            stopwatch.Stop();
                            TimeSpan timeTaken = stopwatch.Elapsed;
                            client.Disconnect();
                            Console.WriteLine("文件已成功下载到本地。");
                            Console.WriteLine($"下载时间: {timeTaken.TotalSeconds} 秒");
                            Application.Current.Dispatcher.Invoke(() =>
                            {
                                Growl.Success("文件下载完成", _token);
                                PrograssVisibility = false;
                            });
                        }
                        catch (Exception ex)
                        {
                            temp = "文件下载失败: " + ex.Message;
                            Console.WriteLine(temp);
                            Application.Current.Dispatcher.Invoke(() =>
                            {
                                Growl.Error(temp, _token);
                                PrograssVisibility = false;
                            });
                        }
                    }
                });
            } catch (Exception ex)
            {
                temp = "文件下载失败: " + ex.Message;
                Console.WriteLine(temp);
                Growl.Error(temp, _token);
            }
        }

原因:
之前的写法中,虽然进度条的值在文件下载过程中被更新,但是因为文件下载操作和 UI 更新都在同一个线程中进行(即主线程),导致 UI 线程被下载操作阻塞(由于UI线程必须在主线程中运行),UI 无法及时刷新,进度条无法显示实时的更新。

具体来说,当文件下载操作进行时,它会占用主线程,这样主线程就没有时间处理其他任务,包括更新 UI。因此,虽然 ProgressValue 被更新了,但是 UI 没有机会重绘进度条。

相关推荐

  1. WPF更新UI线实现进度功能

    2024-07-10 23:16:02       10 阅读
  2. qt 子线更新ui举例

    2024-07-10 23:16:02       5 阅读
  3. 【Android 线】在子线更新UI

    2024-07-10 23:16:02       42 阅读

最近更新

  1. 揭秘Odoo OWL的魔法:reactive vs useState

    2024-07-10 23:16:02       0 阅读
  2. PS像素图层简介

    2024-07-10 23:16:02       0 阅读
  3. ArduPilot开源代码之AP_DAL研读系列

    2024-07-10 23:16:02       0 阅读
  4. Dockerfile相关命令

    2024-07-10 23:16:02       0 阅读
  5. Lucene 索引文件详解:结构与工作原理

    2024-07-10 23:16:02       0 阅读
  6. go语言的命名规则

    2024-07-10 23:16:02       0 阅读

热门阅读

  1. mysql 导出导入 数据库

    2024-07-10 23:16:02       8 阅读
  2. python-django-LlamaIndex 精简版

    2024-07-10 23:16:02       4 阅读
  3. 深入Django(五)

    2024-07-10 23:16:02       9 阅读
  4. Django之登录权限系统

    2024-07-10 23:16:02       6 阅读
  5. LeetCode 35, 242, 994

    2024-07-10 23:16:02       8 阅读
  6. tcp 中的poll机制介绍

    2024-07-10 23:16:02       7 阅读
  7. python excel openpyxl

    2024-07-10 23:16:02       6 阅读
  8. 生物环保的技术原理和优点是什么

    2024-07-10 23:16:02       7 阅读