【问题标题】:ProgressBar value is changing but the gui is not changing on WPFProgressBar 值正在改变,但 gui 在 WPF 上没有改变
【发布时间】:2018-06-09 15:47:42
【问题描述】:

我正在尝试实现ProgressBar,它将在文件传输期间显示。

文件传输是通过BackgroundWorker完成的。

只是为了检查我尝试从主线程取得进展的功能,但没有任何反应。

代码:

myProgressBar.Minimum = 0;
myProgressBar.Maximum = numOfPackets;

这是后台工作代码...

然后:

myProgressBar.IsEnabled = true;

for (int i = 0; i < buff.Length; i++)
{
    myProgressBar.Value = i;
    myProgressBar.UpdateDefaultStyle();
}

为什么什么都没有改变?

【问题讨论】:

  • 您确定buff.Length 大于零吗? numOfPackets也是如此?

标签: c# wpf


【解决方案1】:

在后台线程上进行 IO 或文件传输是非常糟糕的,因为在文件传输或 IO 操作期间,CPU 没有得到使用,所以你的后台线程除了等待 IO 完成之外什么也没做,而你又浪费了一个线程导致内存浪费。另一个缺点是您的 UI 将变得对文件传输没有响应需要相当长的时间,即使您是在后台线程上执行此操作。

我可以继续写在后台线程上执行 IO 工作的缺点,但您应该索取一份关于在 .Net 中异步执行 IO 的文档。 后台线程用于 CPU 密集型工作,例如涉及 CPU 的繁重计算。

您应该使用异步 IO。使用异步和等待。请阅读异步 IO。 我有一个示例,说明您使用带有进度条和响应式 UI 的纯异步 IO 所做的任何事情。我明天写。

** 以下是异步 io 文件传输代码,假设所有文件大小相同以进行进度更新 下面提到的代码将起作用,即使您在传输文件时移动窗口,应用程序也不会挂起。 **

MainWindow.xaml

<Window x:Class="asyncawait.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="514" Width="674">
    <Grid Margin="0,0,0,4">
        <ScrollViewer Name="scrv_Log" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"  HorizontalAlignment="Left" Width="397" Margin="33,61,0,67">
            <ListBox VirtualizingPanel.VirtualizationMode="Recycling" ItemsSource="{Binding Datas,Mode=OneWay}" Background="White" Foreground="Red"/>
        </ScrollViewer>
        <TextBlock Height="40" Width="70" FontSize="10" Foreground="Green" Text="{Binding FileCounter,Mode=OneWay}" Margin="540,281,56,158"></TextBlock>
        <Button Foreground="Black" IsEnabled="{Binding IsButtonEnabled}" Click="Button_Click"  Height="40" Width="40" RenderTransformOrigin="4.65,0.425" Margin="540,210,86,229"></Button>
        <ProgressBar Value="{Binding Progressvalue,Mode=OneWay}"  Name="prg" Foreground="Green"  Height="20" Width="600" Margin="23,453,44.2,7"></ProgressBar>
        <Label Content="{Binding ElementName=prg,Path=Value}" ContentStringFormat="{}{0}%" Height="25" Margin="253,0,295.2,22"  VerticalContentAlignment="Center" VerticalAlignment="Bottom"/>
        <TextBox Background='AliceBlue' HorizontalAlignment="Left" Height="23" Margin="525,109,0,0" TextWrapping="Wrap"  VerticalAlignment="Top" Width="97"/>
    </Grid>
</Window>

****这是执行异步 IO 的代码**

public partial class MainWindow: Window,INotifyPropertyChanged
    {

        public MainWindow()
        {
            InitializeComponent();
            this.DataContext = this;
        }
        public double Progressvalue
        {
            get
            {
                return _Progressvalue;

            } 
            set
            {
                if(value!=_Progressvalue)
                {
                    _Progressvalue=value;
                    OnPropertyChanged();
                }
            }
         }
        private Double _Progressvalue=0;
        private bool _IsEnabled=true;

        public Boolean IsButtonEnabled { 
            get
            {
                return _IsEnabled;
             }

            set
            {
                if(value!=_IsEnabled)
                {
                    _IsEnabled = value;
                    OnPropertyChanged();
                }
            }
        }
        private async  void Button_Click(object sender, RoutedEventArgs e)
        {
            IsButtonEnabled = false;
            await AsyncTransferFiles();
            IsButtonEnabled = true;
            scrv_Log.ScrollToBottom();

        }

        private String fileCounter;
         public String FileCounter
        {
            get
            { return fileCounter; }
            set
            {
                if (value != fileCounter)
                {
                    fileCounter = value;
                    OnPropertyChanged();
                }
            }
        }







        private ObservableCollection<String> _Datas = new ObservableCollection<string>();
        public ObservableCollection<String> Datas
        {
            get
            {
                return _Datas;
            }
        }
         private async Task AsyncTransferFiles()
        {

            var fileNames = Directory.GetFiles("C:\\Data1").ToList();
            int totalCount = fileNames.Count;
            pr = (double)1 / totalCount;
            int counter = 0;
            var progress = new Progress<double>();
            progress.ProgressChanged += (sender, e) =>
                {

                   Progressvalue = Double.Parse(e.ToString());

                };
            foreach (var fileName in fileNames)
            {
                await (CopyFileAsync(fileName, "C:\\GradebookTemp1\\" + fileName.Split('\\')[2], progress, ++counter));
            }
        }



         double pr = 0.0;
         public async Task CopyFileAsync(string sourcePath, string destinationPath,IProgress<double> progress ,int fileCounter)
         {
             using (Stream source = File.Open(sourcePath,FileMode.Open))
             {
                 using (Stream destination = File.Create(destinationPath))
                 {
                     await source.CopyToAsync(destination);
                     progress.Report((int)(pr*fileCounter*100));
                     FileCounter = fileCounter.ToString();
                     Datas.Add("Copied File: " + sourcePath);
                     scrv_Log.ScrollToBottom();

                 }
             }

         }

        private void EnableButton()
        {
            IsButtonEnabled = true;
        }

        private void OnPropertyChanged([CallerMemberName] String propertyName=null)
        {
            var handler = PropertyChanged;
            if(null!=handler)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));

            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
    }

【讨论】:

  • 非常感谢您的指导!我会研究异步/等待任务,我期待您提供的示例,非常感谢!我想用我的代码实现最佳实践。
  • 非常感谢!我试试看
  • 请点赞,以便对其他人有所帮助
【解决方案2】:

该解决方案对我有用一次:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private void button_Click(object sender, RoutedEventArgs e)
    {
        for (int i = 0; i < 100; i++)
        {
            progBar.Value = i;
            progBar.Refresh();
            Thread.Sleep(10);
        }
    }

}

public static class ExtensionMethods
{
    private static Action EmptyDelegate = delegate () { };

    public static void Refresh(this UIElement uiElement)
    {
        uiElement.Dispatcher.Invoke(DispatcherPriority.Render, EmptyDelegate);
    }
}

我的关联示例-XAML 基本上包含:

   <Grid>
    <ProgressBar x:Name="progBar"  HorizontalAlignment="Left" Height="10" Margin="116,175,0,0" VerticalAlignment="Top" Width="100"/>
    <Button x:Name="button" Content="Button" HorizontalAlignment="Left" Margin="156,51,0,0" VerticalAlignment="Top" Width="75" Click="button_Click"/>

</Grid>

但我认为这甚至不是理解所必需的——我想你明白了代码的重点,它是自我描述的

【讨论】:

  • 试过了,只能在新窗口上运行。在我的应用程序中,它仍然没有移动
  • 不要在表单的事件处理程序中使用 Thread.Sleep。这会阻止用户界面
【解决方案3】:

对于 wpf,你可以使用这个:

this.Dispacher.BeginInvoke(new Action(()=> { for(int i...)...; }));

【讨论】:

    【解决方案4】:

    只需Refresh 控制:

    myProgressBar.Refresh();
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-07-01
      • 1970-01-01
      • 1970-01-01
      • 2014-02-28
      • 1970-01-01
      • 2018-05-07
      • 2019-11-30
      相关资源
      最近更新 更多