0

UIにプログレスバーを表示しながら、バックグラウンドスレッドでファイルを読み込もうとしています。BinaryFormatter.Deserialize関数と更新ProgressBarはSTAスレッドで実行する必要があるようです。TPLライブラリを使用してTをロードタスクに渡しますが、これにより、更新とファイルのロードが同じaskScheduler.FromCurrentSynchronizationContext()スレッドにスケジュールされるため、並列ではなくシリアルに実行されるようです。 progressbar

TaskScheduler.Default代わりにパスを試みましたLoadModelTaskが、これにより、BinaryFormatter.Deserialize呼び出しでSTAエラーが発生します。

UIスレッドをフリーズする必要のないバックグラウンドでWPFオブジェクトをロードする別の方法はありますか?

私のコード:

        private void openFile()
        {
            OpenFileDialog dialog = new OpenFileDialog();
            dialog.DefaultExt = FILE_EXTENSION;
            dialog.Filter = "MFlow Documents|*.mpex;*.mpxc;*.mpoc";

            Nullable<bool> result = dialog.ShowDialog();
            if (result == true)
            {                
                string filename = dialog.FileName;

                var scheduler = TaskScheduler.FromCurrentSynchronizationContext();
                CancellationTokenSource source=new CancellationTokenSource();
                CancellationToken token = source.Token;

                feedbackWindow = new FeedbackWindow();
                feedbackWindow.ProgressBar.IsIndeterminate = true;
                feedbackWindow.ProgressLabel.Content = "Opening " + filename;
                feedbackWindow.Show();

                Task<Model> loadModelTask=
                    Task.Factory.StartNew<Model>(() => LoadModel(filename),
                    token, TaskCreationOptions.None, TaskScheduler.FromCurrentSynchronizationContext());
                loadModelTask.ContinueWith(task => AfterLoadModel(task), scheduler);

            }
        }

        private static Model LoadModel(string filename)
        {
            Model returnModel;
            string extension = filename.Split('.')[filename.Split('.').Length - 1];

            Stream stream = File.Open(filename, FileMode.Open);
                using (var gZipStream = new GZipStream(stream, CompressionMode.Decompress))
                {
                    BinaryFormatter formatter = new BinaryFormatter();

                    stream.Seek(0, SeekOrigin.Begin);
                    var test = formatter.Deserialize(gZipStream);
                    returnModel = (Model)formatter.Deserialize(gZipStream);

                    gZipStream.Close();
                    stream.Close();
                }
            }
            return returnModel;
        }

        private void AfterLoadModel(Task<Model> task)
        {
            try
            {
                task.Wait();
                switch (task.Status)
                {
                    case TaskStatus.RanToCompletion:
                        ModelResult = task.Result;
                        feedbackWindow.Close();
                        break;
                    default:
                        break;
                }

            }
            catch (AggregateException ex)
            {
                // For demonstration purposes, show the OCE message. 
                foreach (Exception v in ex.InnerExceptions)
                {
                    Debug.WriteLine("msg: " + v.Message);
                }
            }
        }
4

1 に答える 1

2

UI スレッドをフリーズする必要のないバックグラウンドで WPF オブジェクトを読み込む別の方法はありますか?

絶対に、そうではありません。

WPF UI 要素にはスレッド アフィニティがあります。これは、インスタンス化されたスレッドを使用してのみ操作できることを意味します。親 UIElement は子 UIElement のメソッドを呼び出すため、ビジュアル ツリー内のルート要素の構築を担当する同じスレッドは、ビジュアル ツリー内のすべてのUIElement を構築する同じスレッドである必要があります。

詳細については、MSDN のこの記事を参照してください。

UIElements をシリアライズ/デシリアライズしている場合は、すでに間違っています。逆シリアル化され、UI にバインドされ、DataTemplates 内に記述された UIElements を介して表示されるモデル (POCO クラス) および ViewModels (基本的にはモデルですが、変更通知を含む) の観点からデータを記述する必要があります。これは一般に MVVM パターンと呼ばれ、UI でのスレッド化について (あまり) 心配することなく (正しく行われた場合)、データを非同期に処理できます。

実行時間の長い操作中に UI を更新しようとしているだけの場合は、操作の進行状況を示すプロパティを UI にバインドし、DispatcherTimer (プロパティの現在の状態を読み取る) を使用して UI を更新することをお勧めします。 )または非同期バインディングを使用してUIを自動的に更新します(INPCプロパティバインディングはUIスレッドへのマーシャリング呼び出しを自動的に処理すると思います)。

最後に、操作にかかる時間が 2 秒未満の場合は、プログレス バーを表示するためだけに速度を落とさないでください。あなたのユーザーはあなたに感謝します。

于 2013-01-07T21:37:36.747 に答える