2

ディレクトリ内のすべての画像のリストを取得し、それらすべてに対して MD5 ダイジェストを実行するルーチンがあります。これには時間がかかるため、プログレス バーを含むウィンドウをポップアップ表示します。プログレス バーは、実行時間の長いルーチンに渡すラムダによって更新されます。

最初の問題は、進行状況ウィンドウが更新されないことでした (これは 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

4 に答える 4

2

Mate は DataBinding を使用して簡単な WPF プログラミングを行います。同じことを説明するMVVM設計パターンを参照してください。

プログレスバーの値のプロパティを、DataContext クラスで定義されたソース プロパティにバインドします。そして、ディスパッチャーが呼び出すメソッドで source プロパティを更新します。

後は WPF エンジンが処理します。

あなたは現在、拘束力のないコードを書いています...

于 2010-03-24T11:23:07.697 に答える
1

私が正しく理解していれば、あなたは今メインスレッドですべての作業を行っています。これは、通常のメッセージポンプ(ディスパッチャー)から(多すぎる)時間を取っていることを意味します。

短い修正はWinFormのApplication.DoEvents()に類似していますが、WPFに相当するものがあるかどうかはわかりません。

より良い解決策はスレッドを使用することであり、その場合、Backgroundworkerが最も簡単なアプローチです。たぶん、そのイベントの問題を拡張します。

于 2010-03-24T12:11:08.190 に答える
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();
      }


   }

于 2010-03-24T13:57:54.123 に答える
1

DispatcherPriorityこれはin に関連していることがわかりましたUpdateProgress。より低いものに変更DispatcherPriority.Renderすると、私の場合DispatcherPriority.Backgroundはうまくいきました。

Henk の答えは、メッセージ ポンプが圧倒された場合、いつ何をすべきかを整理する助けが必要であると私に信じさせました。優先度の変更は単なるチケットのようです。

于 2010-03-24T13:01:44.500 に答える