2010-03-24 114 views
2

我有一個例程,它抓取目錄中所有圖像的列表,然後在所有圖像上運行MD5摘要。由於這需要一段時間,我彈出一個帶有進度條的窗口。進度條由我傳入長時間運行的lambda進行更新。首先wpf窗口刷新工作,然後停止

第一個問題是進度窗口從未更新過(這在WPF中是正常的我猜)。由於WPF缺少Refresh()命令,我通過調用Dispatcher.Invoke()來解決這個問題。現在進度條會更新一段時間,然後窗口停止更新。長期工作最終會結束,窗戶恢復正常。

我已經嘗試過一個BackgroundWorker,並且很快就被與長時間運行的進程觸發的事件相關的線程問題沮喪。所以如果這真的是最好的解決方案,我只需要更好地學習範例,請說出來。

但是我會非常樂意使用我在這裏得到的方法,除了在稍後停止更新(例如,在包含1000個文件的文件夾中,它可能會更新50-100個文件,然後「掛」)。在此活動中,用戶界面不需要做出響應,除了報告進度。

無論如何,這是代碼。這也是正是如此叫

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); 
     } 
    } 
} 

:一是進度窗口本身:

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)

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(); 

回答

2

原來,這與UpdateProgress中的DispatcherPriority有關。將DispatcherPriority.Render更改爲更低的值,在我的情況下,DispatcherPriority.Background完成了任務。

亨克的回答讓我相信,如果消息泵不堪重負,它需要幫助理清什麼時候該做什麼。改變優先級似乎只是票。

1

mate以做簡單的編程WPF數據綁定。 請參閱解釋相同的MVVM設計模式。

使用DataContext類中定義的某些源屬性綁定progressbar值屬性。 並更新調度程序調用的方法中的源屬性。

WPF引擎會照顧好休息。

您目前編寫的代碼wothout任何約束力......

+0

您知道我在哪裏可以找到這個工作示例嗎?我嘗試了很多不同的東西來讓它在沒有爲代碼添加間接層的情況下工作。每次酒吧都沒有更新。 – mlibby 2010-03-24 13:03:51

+0

嗨,我發佈了代碼。覈實。 – RockWorld 2010-03-24 14:17:34

1

如果我理解正確的話,你做的主線程上所有的工作了。這意味着你正在從正常的Messagepump(分派器)中拿走(太多)時間。

該修補程序可能是WinForm的Application.DoEvents()的類比,但我不知道是否存在WPF等效項。

更好的解決方案是使用線程,然後Backgroundworker是最簡單的方法。也許擴大這個事件問題。

+0

謝謝!正如你可以看到你的答案幫助我找出問題所在。 – mlibby 2010-03-24 13:02:56

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&ltint, 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&ltint, int> ActionDelegate=null; 
     public static void ImportFolder(string folderPath, Action&ltint, 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(); 
     } 


    }