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

news/2024/7/8 9:22:51 标签: 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 没有机会重绘进度条。


http://www.niftyadmin.cn/n/5536906.html

相关文章

【Eureka服务治理深度解析】探索灰度发布的实现之道

标题&#xff1a;【Eureka服务治理深度解析】探索灰度发布的实现之道 Eureka作为Netflix开源的服务发现框架&#xff0c;在微服务架构中扮演着核心角色。灰度发布作为一种重要的部署策略&#xff0c;允许逐步推出新版本的服务&#xff0c;以减少对用户的影响并提高系统的稳定性…

AWS云创建EC2与所需要注意事项

AWS云&#xff08;Amazon Web Services&#xff09;作为全球领先的云计算服务提供商&#xff0c;为用户提供了丰富的云计算服务。其中&#xff0c;EC2&#xff08;Elastic Compute Cloud&#xff09;是AWS云中的一项重要服务&#xff0c;可以帮助用户轻松创建和管理虚拟服务器实…

SQL SERVER 设置端口

要在SQL Server中设置端口&#xff0c;可以通过SQL Server Configuration Manager来完成。以下是详细的步骤&#xff1a; 1. 打开SQL Server Configuration Manager 在Windows中&#xff0c;按 Win R 键打开运行窗口。输入 SQLServerManager<version>.msc 并按回车。例…

Nginx对冒号特殊字符的处理问题

location /minio/weboffice/weboffice/shapes/ {proxy_pass http://127.0.0.1/minio/weboffice/weboffice/shapes/;}location /minio/weboffice/weboffice/shapes/ {proxy_pass http://127.0.0.1;}nginx对于上诉两种方式&#xff0c;实现的转发效果是一样的&#xff0c;但是在路…

【SpringCloud】Ribbon源码解析

ribbon是一个负载均衡组件&#xff0c;它可以将请求分散到多个服务提供者实例中&#xff0c;提高系统的性能和可用性。本章分析ribbon是如何实现负载均衡的 1、LoadBalanced 消费者在引入ribbon组件后&#xff0c;给http客户端添加LoadBalanced注解就可以启用负载均衡功能。Lo…

一些感想。

1.double必须用double的输出&#xff08;“%lf”&#xff09; 我还以为是什么bug。。 2.sqrt&#xff0c;pow只要include cmath之后就能用了&#xff0c;我pow()没有devc艹的提示&#xff0c;还以为我记错了&#xff0c;早知道运行一下了 cnm公式写错了 #include <iostre…

MacOS下更新curl

苹果自带的curl不支持Https&#xff0c;我们可以通过curl -V看到如下结果 curl 7.72.0 (x86_64-apple-darwin18.6.0) libcurl/7.72.0 zlib/1.2.12 libidn2/2.3.7 librtmp/2.3 Release-Date: 2020-08-19 Protocols: dict file ftp gopher http imap ldap ldaps pop3 rtmp rtsp …

MISRA C 和MISRA C++:汽车软件安全的守护者

一、MISRA C与C语言 自1972年Dennis MacAlistair Ritchie在美国贝尔实验室创造C语言以来&#xff0c;它已成为当今最流行的编程语言之一。C语言以其使用的灵活性、功能的丰富性而广受欢迎&#xff0c;但同时也因其宽松的语法和不严格的数据类型给开发的产品带来了安全隐患。 …