2010-03-24 2 views
2

У меня есть рутина, которая захватывает список всех изображений в каталоге, а затем запускает дайджест MD5 на всех из них. Поскольку это занимает некоторое время, я должен открыть окно с индикатором выполнения. Индикатор выполнения обновляется лямбдой, которую я перехожу к длительной рутине.Сначала обновляется окно wpf, затем останавливается

Первой проблемой было то, что окно прогресса никогда не обновлялось (что, как я полагаю, является нормальным в WPF). Поскольку WPF не хватает команды Refresh(), я исправил это с помощью вызова Dispatcher.Invoke(). Теперь индикатор выполнения обновляется некоторое время, затем окно перестает обновляться. В конечном итоге работа над долгой работой заканчивается, и окна возвращаются к нормальной жизни.

Я уже пробовал BackgroundWorker и быстро разочаровался в проблеме потоковой передачи, связанной с событием, вызванным длительным процессом. Так что если это действительно лучшее решение, и мне просто нужно лучше изучить парадигму, скажите об этом.

Но я был бы намного более счастлив с подходом, который я получил здесь, за исключением того, что он немного перестает обновляться (например, в папке с 1000 файлами она может обновляться для 50-100 файлов, затем "повесить"). Пользовательский интерфейс не должен быть отзывчивым во время этой деятельности, за исключением отчета о прогрессе.

В любом случае, вот код. Сначала само окно прогресса:

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

ответ

2

Оказывается, это связано с DispatcherPriority в UpdateProgress. Изменив DispatcherPriority.Render на что-то ниже, в моем случае DispatcherPriority.Background сделал трюк.

Ответ Хенка заставил меня поверить, что если насос сообщений перегружен, ему нужна помощь в сортировке, что делать, когда. Изменение приоритета - это просто билет.

1

Mate сделать простое программирование WPF с помощью DataBinding. Обратитесь к шаблону проектирования MVVM, объясняя его.

Связать свойство свойства progress с некоторым исходным свойством, определенным в классе DataContext. И обновить свойство source в методе, вызванном диспетчером.

Двигатель WPF позаботится об отдыхе.

Вы в настоящее время написали код wothout любого связывающего ...

+0

Вы знаете, где я могу найти рабочий пример этого? Я попробовал много разных вещей, чтобы заставить это работать без добавления слоев косвенности к коду. Каждый раз, когда панель вообще не обновлялась. – mlibby

+0

Привет, я разместил код. Проверь это. – RockWorld

1

Если я правильно понимаю, что вы делаете всю работу на главном потоке в настоящее время. Это означает, что вы убираете (слишком много) время от обычного Messagepump (Диспетчер).

Краткое исправление будет аналогом приложения WinForm Application.DoEvents(), но я не знаю, есть ли эквивалент WPF.

Лучшим решением было бы использовать Thread, а затем Backgroundworker - самый простой способ. Возможно, расширится эта проблема.

+0

Спасибо! Как вы видите, ваш ответ помог мне разобраться в проблеме. – mlibby

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


    } 
 
Смежные вопросы