WPFを使用してC#でBitmapImageを非同期に読み込む最良の方法は何ですか?多くの解決策が存在するようですが、標準パターンまたはベストプラクティスが存在しますか?WPFを使用してC#でBitmapImageを非同期に読み込む
ありがとう!
WPFを使用してC#でBitmapImageを非同期に読み込む最良の方法は何ですか?多くの解決策が存在するようですが、標準パターンまたはベストプラクティスが存在しますか?WPFを使用してC#でBitmapImageを非同期に読み込む
ありがとう!
データバインディングを使用していると仮定すると、Binding.IsAsyncプロパティをTrueに設定するのが標準的な方法です。 あなたがバックグラウンドスレッド+ディスパッチャオブジェクトを使用してコードビハインドファイルにビットマップをロードしている場合は、UI非同期
使用またはSystem.ComponentModel.BackgroundWorkerを拡張を更新するための一般的な方法です:
個人的に http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx
、私は、これがクライアントアプリケーションで非同期操作を実行する最も簡単な方法であると考えています。 WPFではなくWPFormでこれを使用しています。これはWPFでもうまくいくと仮定しています。
私は通常、Backgroundworkerを拡張していますが、その必要はありません。
public class ResizeFolderBackgroundWorker : BackgroundWorker
{
public ResizeFolderBackgroundWorker(string sourceFolder, int resizeTo)
{
this.sourceFolder = sourceFolder;
this.destinationFolder = destinationFolder;
this.resizeTo = resizeTo;
this.WorkerReportsProgress = true;
this.DoWork += new DoWorkEventHandler(ResizeFolderBackgroundWorker_DoWork);
}
void ResizeFolderBackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
DirectoryInfo dirInfo = new DirectoryInfo(sourceFolder);
FileInfo[] files = dirInfo.GetFiles("*.jpg");
foreach (FileInfo fileInfo in files)
{
/* iterate over each file and resizing it */
}
}
}
これは、あなたのフォームでそれを使用する方法である:
//handle a button click to start lengthy operation
private void resizeImageButtonClick(object sender, EventArgs e)
{
string sourceFolder = getSourceFolderSomehow();
resizer = new ResizeFolderBackgroundWorker(sourceFolder,290);
resizer.ProgressChanged += new progressChangedEventHandler(genericProgressChanged);
resizer.RunWorkerCompleted += new RunWorkerCompletedEventHandler(genericRunWorkerCompleted);
progressBar1.Value = 0;
progressBar1.Visible = true;
resizer.RunWorkerAsync();
}
void genericRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
progressBar1.Visible = false;
//signal to user that operation has completed
}
void genericProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
//I just update a progress bar
}
ここでは、カツオの答えに詳しく説明するためには、のisAsyncを設定する場所へと小さな例です。
ItemsSource="{Binding IsAsync=True,Source={StaticResource ACollection},Path=AnObjectInCollection}"
これはXAMLで行うことです。
なぜ私はこの答えが-1だったのか分かりません。変換の非同期を行う方法を理解できませんでした。あなたの束縛の中でIsAsync = Trueを入れなければならないことが分かります。どうもありがとうございました! – Rasive
私はちょうどこれを見ていて、私の2セントを投げなければなりませんでしたが、元のポストの数年後ですが(ちょうど誰かが私が探していた同じことを探している場合)。
私はそれが画像ストリームを使用してバックグラウンドでロードされ、その後、表示されたのです持っている必要があります画像コントロールを持っています。私はに走り続け
問題がたBitmapSourceが、それはストリームソースとすべて同じスレッド上でなければなりませんでした画像制御であるということです。
この場合、バインディングを使用してIsAsynch = trueに設定すると、クロススレッド例外がスローされます。
BackgroundWorkerはWinFormsに最適です。これをWPFで使用できますが、私はWPFでWinFormアセンブリを使用することを避けます(プロジェクトの膨張はお勧めできません)。この場合も無効な相互参照例外がスローされるはずですが、テストしませんでした。
//Create the image control
Image img = new Image {HorizontalAlignment = System.Windows.HorizontalAlignment.Stretch, VerticalAlignment = System.Windows.VerticalAlignment.Stretch};
//Create a seperate thread to load the image
ThreadStart thread = delegate
{
//Load the image in a seperate thread
BitmapImage bmpImage = new BitmapImage();
MemoryStream ms = new MemoryStream();
//A custom class that reads the bytes of off the HD and shoves them into the MemoryStream. You could just replace the MemoryStream with something like this: FileStream fs = File.Open(@"C:\ImageFileName.jpg", FileMode.Open);
MediaCoder.MediaDecoder.DecodeMediaWithStream(ImageItem, true, ms);
bmpImage.BeginInit();
bmpImage.StreamSource = ms;
bmpImage.EndInit();
//**THIS LINE locks the BitmapImage so that it can be transported across threads!!
bmpImage.Freeze();
//Call the UI thread using the Dispatcher to update the Image control
Dispatcher.BeginInvoke(new ThreadStart(delegate
{
img.Source = bmpImage;
img.Unloaded += delegate
{
ms.Close();
ms.Dispose();
};
grdImageContainer.Children.Add(img);
}));
};
//Start previously mentioned thread...
new Thread(thread).Start();
BackgroundWorkerは、Windowsフォームではなく、System.dllのSystem.ComponentModel名前空間で定義されているため、WPF用に使用することもできます。 –
BackgroundWorkerまたはThreadStartを使用するとコードが多すぎます。 'ThreadPool.QueueUserWorkItem(_ => {...}) 'を投げてください。あなたのスレッドがあります。 – SandRock
BitmapCacheOption.OnLoad
var bmp = await System.Threading.Tasks.Task.Run(() =>
{
BitmapImage img = new BitmapImage();
img.BeginInit();
img.CacheOption = BitmapCacheOption.OnLoad;
img.UriSource = new Uri(path);
img.EndInit();
ImageBrush brush = new ImageBrush(img);
}
これは、あなたが非同期ダウンロードを行うためのHttpClientを使用してUIスレッドでのBitmapImageを作成できるようになります:
は、1行のコードは、これらの作業のいずれかになりますことが判明します:
private async Task<BitmapImage> LoadImage(string url)
{
HttpClient client = new HttpClient();
try
{
BitmapImage img = new BitmapImage();
img.CacheOption = BitmapCacheOption.OnLoad;
img.BeginInit();
img.StreamSource = await client.GetStreamAsync(url);
img.EndInit();
return img;
}
catch (HttpRequestException)
{
// the download failed, log error
return null;
}
}
のBitmapImageは、UIスレッド上で作成する必要があります。これは... Jmix90 @ –
を例外がスローされます:それはあなたが参照しているこの回答のどの部分は明らかではありません。ただし、 'Binding.IsAsync'を使用すると、Uri /パスを' Image.Source'で指定して、画像を自動的に非同期にロードすることができます。このシナリオでは、クロススレッドの問題をWPFが処理します。バックグラウンドスレッドでビットマップを作成している場合、ビットマップオブジェクトをUIスレッドに渡す前にビットマップオブジェクトで 'Freeze()'を呼び出す必要があります。あなたが間違っていると例外を投げるだけです。 –