wpf窗口刷新首先工作,然后停止

时间:2010-03-24 11:18:49

标签: c# wpf progress-bar long-running-processes

我有一个例程,它抓取目录中所有图像的列表,然后在所有图像上运行MD5摘要。由于这需要一段时间,我会弹出一个带有进度条的窗口。进度条由lambda更新,我传入长期运行的例程。

第一个问题是进度窗口从未更新过(我认为这在WPF中是正常的)。由于WPF缺少Refresh()命令,因此我通过调用Dispatcher.Invoke()来修复此问题。现在进度条已更新一段时间,然后窗口停止更新。长时间运行的工作最终完成,窗户恢复正常。

我已经尝试过BackgroundWorker,并且很快就会遇到与长时间运行的流程触发的事件相关的线程问题。因此,如果这真的是最好的解决方案,我只需要更好地学习范例,请说出来。

但是我对这里的方法感到非常高兴,除了它稍微停止更新后(例如,在包含1000个文件的文件夹中,它可能会更新50-100个文件,然后“挂”)。除了报告进度外,UI不需要在此活动期间做出响应。

无论如何,这是代码。首先是进度窗口本身:

public partial class ProgressWindow : Window
{
    public ProgressWindow(string title, string supertext, string subtext)
    {
        InitializeComponent();
        this.Title = title;
        this.SuperText.Text = supertext;
        this.SubText.Text = subtext;
    }

    internal void UpdateProgress(int count, int total)
    {
        this.ProgressBar.Maximum = Convert.ToDouble(total);
        this.ProgressBar.Value = Convert.ToDouble(count);
        this.SubText.Text = String.Format("{0} of {1} finished", count, total);
        this.Dispatcher.Invoke(DispatcherPriority.Render, EmptyDelegate);
    }

    private static Action EmptyDelegate = delegate() { };
}


<Window x:Class="Pixort.ProgressWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Pixort Progress" Height="128" Width="256" WindowStartupLocation="CenterOwner" WindowStyle="SingleBorderWindow" ResizeMode="NoResize">
    <DockPanel>
        <TextBlock DockPanel.Dock="Top" x:Name="SuperText" TextAlignment="Left" Padding="6"></TextBlock>
        <TextBlock DockPanel.Dock="Bottom" x:Name="SubText" TextAlignment="Right" Padding="6"></TextBlock>
        <ProgressBar x:Name="ProgressBar" Height="24" Margin="6"/>
    </DockPanel>
</Window>

长时间运行的方法(在Gallery.cs中):

public void ImportFolder(string folderPath, Action<int, int> progressUpdate)
{
    string[] files = this.FileIO.GetFiles(folderPath);

    for (int i = 0; i < files.Length; i++)
    {
        // do stuff with the file
        if (null != progressUpdate)
        {
            progressUpdate.Invoke(i + 1, files.Length);
        }
    }
}

这就是所谓的:

 ProgressWindow progress = new ProgressWindow("Import Folder Progress", String.Format("Importing {0}", folder), String.Empty);
 progress.Show();
 this.Gallery.ImportFolder(folder, ((c, t) => progress.UpdateProgress(c, t)));
 progress.Close();

4 个答案:

答案 0 :(得分:2)

原来这与DispatcherPriority中的UpdateProgress有关。将DispatcherPriority.Render更改为更低级别,在我的情况下DispatcherPriority.Background就可以了。

Henk的回答让我相信,如果消息泵不堪重负,它需要帮助整理出什么时候做什么。改变优先权似乎只是门票。

答案 1 :(得分:1)

Mate使用DataBinding进行简单的WPF编程。 请参阅解释相同的MVVM设计模式。

使用DataContext类中定义的某些source属性绑定progressbar value属性。 并在调度程序调用方法中更新source属性。

WPF引擎将负责休息。

您目前编写的代码没有任何绑定......

答案 2 :(得分:1)

如果我理解正确,您现在就在主线程上完成所有工作。这意味着你从正常的Messagepump(Dispatcher)中拿走(太多)时间。

简短修复与WinForm的Application.DoEvents()类似,但我不知道是否有WPF等效。

更好的解决方案是使用Thread,然后Backgroundworker是最简单的方法。也许扩展该事件问题。

答案 3 :(得分:1)

用于执行预期操作的修改代码。 [注意:Xaml代码未被修改]


 public partial class ProgressWindow : Window
   {
      public ProgressWindow(string title, string supertext, string subtext)
      {
         InitializeComponent();
         EmptyDelegate = RaiseOnDispatcher;
        this.Title = title; 
        this.SuperText.Text = supertext; 
        this.SubText.Text = subtext; 
      }


    internal void UpdateProgress(int count, int total) 
    {
       this.Dispatcher.Invoke(EmptyDelegate,DispatcherPriority.Render,new object[]{count,total}); 
    }

    private static Action<int, int> EmptyDelegate = null;

    private void RaiseOnDispatcher(int count, int total)
    {
       this.ProgressBar.Maximum = Convert.ToDouble(total);
       this.ProgressBar.Value = Convert.ToDouble(count);
       this.SubText.Text = String.Format("{0} of {1} finished", count, total);
    }
   }


   public class Gallery
   {
      static Action<int, int> ActionDelegate=null;
      public static void ImportFolder(string folderPath, Action<int, int> progressUpdate)
      {
         ActionDelegate = progressUpdate;
         BackgroundWorker backgroundWorker = new BackgroundWorker();
         backgroundWorker.DoWork += new DoWorkEventHandler(worker_DoWork);
         backgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_WorkCompleted);
         backgroundWorker.RunWorkerAsync(folderPath);
         backgroundWorker = null;
      }

     static void worker_DoWork(object sender, DoWorkEventArgs e)
      {
         string folderPath = e.Argument.ToString();
         DirectoryInfo dir = new DirectoryInfo(folderPath);
         FileInfo[] files = dir.GetFiles();

         for (int i = 0; i < files.Length; i++)
         {
            // do stuff with the file 
            Thread.Sleep(1000);// remove in actual implementation
            if (null != ActionDelegate)
            {
               ActionDelegate.Invoke(i + 1, files.Length);
            }
         }
      }
      static void worker_WorkCompleted(object sender, RunWorkerCompletedEventArgs e)
      {
         //do after work complete
      }

      public static void Operate()
      {
         string folder = "folderpath";
         ProgressWindow progress = new ProgressWindow("Import Folder Progress", String.Format("Importing {0}", folder), String.Empty);
         progress.Show();
         Gallery.ImportFolder(folder, ((c, t) => progress.UpdateProgress(c, t)));
         progress.Close();
      }


   }