Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Cet exemple indique comment utiliser la méthode TaskScheduler.FromCurrentSynchronizationContext dans une application Windows Presentation Foundation (WPF) pour planifier une tâche sur le même thread sur lequel le contrôle d'interface utilisateur a été créé.
Procédures
Pour créer le projet WPF
Dans Visual Studio, créez un projet d'application WPF et nommez-le.
En mode Design, faites glisser un contrôle Image de la Boîte à outils vers l'aire de conception. Dans la vue XAML, spécifiez l'alignement horizontal comme « Left ». La taille n'a pas d'importance parce que le contrôle sera dynamiquement redimensionné au moment de l'exécution. Acceptez le nom par défaut, « image1 ».
Faites glisser un bouton de la Boîte à outils vers la partie inférieure gauche de la fenêtre d'application. Double-cliquez sur le bouton pour ajouter un gestionnaire d'événements Click. Dans la vue XAML, spécifiez la propriété Content du bouton comme « Make a Mosaic » et spécifiez son alignement horizontal comme « Left ».
Dans le fichier MainWindow.xaml.cs, utilisez le code suivant pour remplacer le contenu entier du fichier. Assurez-vous que le nom de l'espace de noms correspond au nom du projet.
Appuyez sur F5 pour exécuter l'application. Chaque fois que vous cliquez sur le bouton, une nouvelle disposition de mosaïques doit être affichée.
Exemple
Description
L'exemple suivant crée une mosaïque des images sélectionnées aléatoirement à partir d'un répertoire spécifié. Les objets WPF sont utilisés pour charger et redimensionner les images. Ensuite les pixels bruts sont passés à une tâche qui utilise une boucle ParallelFor() pour écrire les données de pixels dans un grand tableau codé sur un octet. Aucune synchronisation n'est obligatoire parce qu'aucune paire de mosaïques n'occupe les mêmes éléments de tableau. Les mosaïques peuvent également être écrites dans n'importe quel ordre parce que leur position est calculée indépendamment de toute autre mosaïque. Le grand tableau est alors passé à une tâche qui s'exécute sur le thread d'interface utilisateur, où les données de pixels sont chargées dans un contrôle Image.
Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace wpfApplication1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private int fileCount;
int colCount;
int rowCount;
private int tilePixelHeight;
private int tilePixelWidth;
private int largeImagePixelHeight;
private int largeImagePixelWidth;
private int largeImageStride;
PixelFormat format;
BitmapPalette palette;
public MainWindow()
{
InitializeComponent();
// For this example, values are hard-coded to a mosaic of 8x8 tiles.
// Each tile is 50 pixels high and 66 pixels wide and 32 bits per pixel.
colCount = 12;
rowCount = 8;
tilePixelHeight = 50;
tilePixelWidth = 66;
largeImagePixelHeight = tilePixelHeight * rowCount;
largeImagePixelWidth = tilePixelWidth * colCount;
largeImageStride = largeImagePixelWidth * (32 / 8);
this.Width = largeImagePixelWidth + 40;
image1.Width = largeImagePixelWidth;
image1.Height = largeImagePixelHeight;
}
private void button1_Click(object sender, RoutedEventArgs e)
{
// For best results use 1024 x 768 jpg files at 32bpp.
string[] files = System.IO.Directory.GetFiles(@"C:\Users\Public\Pictures\Sample Pictures\", "*.jpg");
fileCount = files.Length;
Task<byte[]>[] images = new Task<byte[]>[fileCount];
for (int i = 0; i < fileCount; i++)
{
int x = i;
images[x] = Task.Factory.StartNew(() => LoadImage(files[x]));
}
// When they�ve all been loaded, tile them into a single byte array.
var tiledImage = Task.Factory.ContinueWhenAll(
images, (i) => TileImages(i));
// We are currently on the UI thread. Save the sync context and pass it to
// the next task so that it can access the UI control "image1".
var UISyncContext = TaskScheduler.FromCurrentSynchronizationContext();
// On the UI thread, put the bytes into a bitmap and
// and display it in the Image control.
var t3 = tiledImage.ContinueWith((antedecent) =>
{
// Get System DPI.
Matrix m = PresentationSource.FromVisual(Application.Current.MainWindow)
.CompositionTarget.TransformToDevice;
double dpiX = m.M11;
double dpiY = m.M22;
BitmapSource bms = BitmapSource.Create( largeImagePixelWidth,
largeImagePixelHeight,
dpiX,
dpiY,
format,
palette, //use default palette
antedecent.Result,
largeImageStride);
image1.Source = bms;
}, UISyncContext);
}
byte[] LoadImage(string filename)
{
// Use the WPF BitmapImage class to load and
// resize the bitmap. NOTE: Only 32bpp formats are supported correctly.
// Support for additional color formats is left as an exercise
// for the reader. For more information, see documentation for ColorConvertedBitmap.
BitmapImage myBitmapImage = new BitmapImage();
myBitmapImage.BeginInit();
myBitmapImage.UriSource = new Uri(filename);
tilePixelHeight = myBitmapImage.DecodePixelHeight = tilePixelHeight;
tilePixelWidth = myBitmapImage.DecodePixelWidth = tilePixelWidth;
myBitmapImage.EndInit();
format = myBitmapImage.Format;
int size = (int)(myBitmapImage.Height * myBitmapImage.Width);
int stride = (int)myBitmapImage.Width * 4;
byte[] dest = new byte[stride * tilePixelHeight];
myBitmapImage.CopyPixels(dest, stride, 0);
return dest;
}
int Stride(int pixelWidth, int bitsPerPixel)
{
return (((pixelWidth * bitsPerPixel + 31) / 32) * 4);
}
// Map the individual image tiles to the large image
// in parallel. Any kind of raw image manipulation can be
// done here because we are not attempting to access any
// WPF controls from multiple threads.
byte[] TileImages(Task<byte[]>[] sourceImages)
{
byte[] largeImage = new byte[largeImagePixelHeight * largeImageStride];
int tileImageStride = tilePixelWidth * 4; // hard coded to 32bpp
Random rand = new Random();
Parallel.For(0, rowCount * colCount, (i) =>
{
// Pick one of the images at random for this tile.
int cur = rand.Next(0, sourceImages.Length);
byte[] pixels = sourceImages[cur].Result;
// Get the starting index for this tile.
int row = i / colCount;
int col = (int)(i % colCount);
int idx = ((row * (largeImageStride * tilePixelHeight)) + (col * tileImageStride));
// Write the pixels for the current tile. The pixels are not contiguous
// in the array, therefore we have to advance the index by the image stride
// (minus the stride of the tile) for each scanline of the tile.
int tileImageIndex = 0;
for (int j = 0; j < tilePixelHeight; j++)
{
// Write the next scanline for this tile.
for (int k = 0; k < tileImageStride; k++)
{
largeImage[idx++] = pixels[tileImageIndex++];
}
// Advance to the beginning of the next scanline.
idx += largeImageStride - tileImageStride;
}
});
return largeImage;
}
}
}
Commentaires
Cet exemple montre comment déplacer des données du thread d'interface utilisateur, le modifier à l'aide des boucles parallèles et des objets Task, puis les repasser à une tâche qui s'exécute sur le thread d'interface utilisateur. Cette approche est utile lorsque vous devez utiliser la bibliothèque parallèle de tâches pour exécuter des opérations qui ne sont pas non plus prises en charge par l'API WPF, ou ne sont pas suffisamment rapides. Une autre méthode pour créer une mosaïque d'images dans WPF consiste à utiliser un objet WrapPanel et à y ajouter des images. Le WrapPanel s'occupera du travail de positionnement des mosaïques. Toutefois, ce travail peut être exécuté uniquement sur le thread d'interface utilisateur.
Cet exemple comporte des limitations. Par exemple, seules les images de 32 bits par pixel sont prises en charge ; les images dans d'autres formats sont endommagées par l'objet BitmapImage pendant l'opération de redimensionnement. De même, les images source doivent être plus grandes que la taille de la mosaïque. En guise d'exercice supplémentaire, vous pouvez ajouter les fonctionnalités permettant de gérer plusieurs formats de pixels et tailles de fichiers.