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

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

我的写法
xml 复制代码
<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>
cpp 复制代码
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建议的写法
cpp 复制代码
// 重复下载会将上次覆盖
        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 没有机会重绘进度条。

相关推荐
冷眼Σ(-᷅_-᷄๑)2 小时前
WPF缩放动画和平移动画叠加后会发生什么?
wpf·动画
△曉風殘月〆4 小时前
WPF MVVM入门系列教程(二、依赖属性)
c#·wpf·mvvm
SoraLuna7 小时前
「Mac畅玩鸿蒙与硬件28」UI互动应用篇5 - 滑动选择器实现
macos·ui·harmonyos
.net开发11 小时前
WPF怎么通过RestSharp向后端发请求
前端·c#·.net·wpf
九鼎科技-Leo11 小时前
WPF 中 NavigationWindow 与 Page 的继承关系解析
wpf
SongYuLong的博客12 小时前
C# WPF 打印机
wpf
就是有点傻12 小时前
WPF中的转换器
wpf
martian66518 小时前
QT开发:掌握现代UI动画技术:深入解析QML和Qt Quick中的动画效果
开发语言·c++·qt·ui
.net开发19 小时前
WPF使用prism框架发布订阅实现消息提示
c#·.net·wpf
初九之潜龙勿用20 小时前
C#结合JS解决Word添加无效位图导致进程停滞的问题
javascript·ui·c#·word·asp.net