İzlenecek yol: Windows Forms Uygulaması'nda Veri Akışı Kullanma

Bu makalede, bir Windows Forms uygulamasında görüntü işleme gerçekleştiren veri akışı bloklarından oluşan bir ağın nasıl oluşturulacağı gösterilmektedir.

Bu örnek, belirtilen klasörden görüntü dosyalarını yükler, bileşik bir görüntü oluşturur ve sonucu görüntüler. Örnek, görüntüleri ağ üzerinden yönlendirmek için veri akışı modelini kullanır. Veri akışı modelinde, bir programın bağımsız bileşenleri ileti göndererek birbirleriyle iletişim kurar. Bir bileşen bir ileti aldığında, bazı eylemler gerçekleştirir ve ardından sonucu başka bir bileşene geçirir. Bunu, uygulamanın bir programdaki işlemlerin sırasını denetlemek için koşullu deyimler, döngüler vb. gibi denetim yapılarını kullandığı denetim akışı modeliyle karşılaştırın.

Önkoşullar

Bu kılavuza başlamadan önce Veri Akışı'nı okuyun.

Not

TPL Veri Akışı Kitaplığı ( System.Threading.Tasks.Dataflow ad alanı) .NET ile dağıtılmaz. Ad alanını System.Threading.Tasks.Dataflow Visual Studio'ya yüklemek için projenizi açın, Projemenüsünden NuGet Paketlerini Yönet'i seçin ve çevrimiçi ortamda paketi arayınSystem.Threading.Tasks.Dataflow. Alternatif olarak, .NET Core CLI kullanarak yüklemek için komutunu çalıştırın dotnet add package System.Threading.Tasks.Dataflow.

Bölümler

Bu izlenecek yol aşağıdaki bölümleri içerir:

Windows Forms Uygulaması Oluşturma

Bu bölümde temel Windows Forms uygulamasının nasıl oluşturulacağı ve ana forma denetimlerin nasıl ekleneceği açıklanır.

Windows Forms Uygulaması Oluşturmak için

  1. Visual Studio'da bir Visual C# veya Visual Basic Windows Forms Uygulaması projesi oluşturun. Bu belgede projenin adı CompositeImagesverilmiştir.

  2. Ana formun form tasarımcısında Form1.cs (Visual Basic için Form1.vb) bir ToolStrip denetim ekleyin.

  3. Denetime ToolStrip bir ToolStripButton denetim ekleyin. DisplayStyle özelliğini olarakText, özelliğini ise TextKlasör Seç olarak ayarlayın.

  4. Denetime ToolStrip ikinci ToolStripButton bir denetim ekleyin. özelliğini olarak, özelliğini cancel, özelliğini ise Enabled olarak Falseayarlayın.TextTextDisplayStyle

  5. Ana forma bir PictureBox nesne ekleyin. Dock özelliğini olarak Fillayarlayın.

Veri Akışı Ağı Oluşturma

Bu bölümde, görüntü işleme gerçekleştiren veri akışı ağının nasıl oluşturulacağı açıklanır.

Veri Akışı Ağı Oluşturmak için

  1. Projenize System.Threading.Tasks.Dataflow.dll başvuru ekleyin.

  2. Form1.cs'nin (Visual Basic için Form1.vb) aşağıdaki using (Using Visual Basic'te) deyimlerini içerdiğinden emin olun:

    using System;
    using System.Collections.Generic;
    using System.Drawing;
    using System.Drawing.Imaging;
    using System.IO;
    using System.Linq;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Threading.Tasks.Dataflow;
    using System.Windows.Forms;
    
  3. Sınıfına aşağıdaki veri üyelerini Form1 ekleyin:

    // The head of the dataflow network.
    ITargetBlock<string> headBlock = null;
    
    // Enables the user interface to signal cancellation to the network.
    CancellationTokenSource cancellationTokenSource;
    
  4. Sınıfına aşağıdaki yöntemi olan CreateImageProcessingNetworköğesini Form1 ekleyin. Bu yöntem görüntü işleme ağını oluşturur.

    // Creates the image processing dataflow network and returns the
    // head node of the network.
    ITargetBlock<string> CreateImageProcessingNetwork()
    {
       //
       // Create the dataflow blocks that form the network.
       //
    
       // Create a dataflow block that takes a folder path as input
       // and returns a collection of Bitmap objects.
       var loadBitmaps = new TransformBlock<string, IEnumerable<Bitmap>>(path =>
          {
             try
             {
                return LoadBitmaps(path);
             }
             catch (OperationCanceledException)
             {
                // Handle cancellation by passing the empty collection
                // to the next stage of the network.
                return Enumerable.Empty<Bitmap>();
             }
          });
    
       // Create a dataflow block that takes a collection of Bitmap objects
       // and returns a single composite bitmap.
       var createCompositeBitmap = new TransformBlock<IEnumerable<Bitmap>, Bitmap>(bitmaps =>
          {
             try
             {
                return CreateCompositeBitmap(bitmaps);
             }
             catch (OperationCanceledException)
             {
                // Handle cancellation by passing null to the next stage
                // of the network.
                return null;
             }
          });
    
       // Create a dataflow block that displays the provided bitmap on the form.
       var displayCompositeBitmap = new ActionBlock<Bitmap>(bitmap =>
          {
             // Display the bitmap.
             pictureBox1.SizeMode = PictureBoxSizeMode.StretchImage;
             pictureBox1.Image = bitmap;
    
             // Enable the user to select another folder.
             toolStripButton1.Enabled = true;
             toolStripButton2.Enabled = false;
             Cursor = DefaultCursor;
          },
          // Specify a task scheduler from the current synchronization context
          // so that the action runs on the UI thread.
          new ExecutionDataflowBlockOptions
          {
              TaskScheduler = TaskScheduler.FromCurrentSynchronizationContext()
          });
    
       // Create a dataflow block that responds to a cancellation request by
       // displaying an image to indicate that the operation is cancelled and
       // enables the user to select another folder.
       var operationCancelled = new ActionBlock<object>(delegate
          {
             // Display the error image to indicate that the operation
             // was cancelled.
             pictureBox1.SizeMode = PictureBoxSizeMode.CenterImage;
             pictureBox1.Image = pictureBox1.ErrorImage;
    
             // Enable the user to select another folder.
             toolStripButton1.Enabled = true;
             toolStripButton2.Enabled = false;
             Cursor = DefaultCursor;
          },
          // Specify a task scheduler from the current synchronization context
          // so that the action runs on the UI thread.
          new ExecutionDataflowBlockOptions
          {
             TaskScheduler = TaskScheduler.FromCurrentSynchronizationContext()
          });
    
       //
       // Connect the network.
       //
    
       // Link loadBitmaps to createCompositeBitmap.
       // The provided predicate ensures that createCompositeBitmap accepts the
       // collection of bitmaps only if that collection has at least one member.
       loadBitmaps.LinkTo(createCompositeBitmap, bitmaps => bitmaps.Count() > 0);
    
       // Also link loadBitmaps to operationCancelled.
       // When createCompositeBitmap rejects the message, loadBitmaps
       // offers the message to operationCancelled.
       // operationCancelled accepts all messages because we do not provide a
       // predicate.
       loadBitmaps.LinkTo(operationCancelled);
    
       // Link createCompositeBitmap to displayCompositeBitmap.
       // The provided predicate ensures that displayCompositeBitmap accepts the
       // bitmap only if it is non-null.
       createCompositeBitmap.LinkTo(displayCompositeBitmap, bitmap => bitmap != null);
    
       // Also link createCompositeBitmap to operationCancelled.
       // When displayCompositeBitmap rejects the message, createCompositeBitmap
       // offers the message to operationCancelled.
       // operationCancelled accepts all messages because we do not provide a
       // predicate.
       createCompositeBitmap.LinkTo(operationCancelled);
    
       // Return the head of the network.
       return loadBitmaps;
    }
    
  5. LoadBitmaps yöntemini uygulayın.

    // Loads all bitmap files that exist at the provided path.
    IEnumerable<Bitmap> LoadBitmaps(string path)
    {
       List<Bitmap> bitmaps = new List<Bitmap>();
    
       // Load a variety of image types.
       foreach (string bitmapType in
          new string[] { "*.bmp", "*.gif", "*.jpg", "*.png", "*.tif" })
       {
          // Load each bitmap for the current extension.
          foreach (string fileName in Directory.GetFiles(path, bitmapType))
          {
             // Throw OperationCanceledException if cancellation is requested.
             cancellationTokenSource.Token.ThrowIfCancellationRequested();
    
             try
             {
                // Add the Bitmap object to the collection.
                bitmaps.Add(new Bitmap(fileName));
             }
             catch (Exception)
             {
                // TODO: A complete application might handle the error.
             }
          }
       }
       return bitmaps;
    }
    
  6. CreateCompositeBitmap yöntemini uygulayın.

    // Creates a composite bitmap from the provided collection of Bitmap objects.
    // This method computes the average color of each pixel among all bitmaps
    // to create the composite image.
    Bitmap CreateCompositeBitmap(IEnumerable<Bitmap> bitmaps)
    {
       Bitmap[] bitmapArray = bitmaps.ToArray();
    
       // Compute the maximum width and height components of all
       // bitmaps in the collection.
       Rectangle largest = new Rectangle();
       foreach (var bitmap in bitmapArray)
       {
          if (bitmap.Width > largest.Width)
             largest.Width = bitmap.Width;
          if (bitmap.Height > largest.Height)
             largest.Height = bitmap.Height;
       }
    
       // Create a 32-bit Bitmap object with the greatest dimensions.
       Bitmap result = new Bitmap(largest.Width, largest.Height,
          PixelFormat.Format32bppArgb);
    
       // Lock the result Bitmap.
       var resultBitmapData = result.LockBits(
          new Rectangle(new Point(), result.Size), ImageLockMode.WriteOnly,
          result.PixelFormat);
    
       // Lock each source bitmap to create a parallel list of BitmapData objects.
       var bitmapDataList = (from bitmap in bitmapArray
                             select bitmap.LockBits(
                               new Rectangle(new Point(), bitmap.Size),
                               ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb))
                            .ToList();
    
       // Compute each column in parallel.
       Parallel.For(0, largest.Width, new ParallelOptions
       {
          CancellationToken = cancellationTokenSource.Token
       },
       i =>
       {
          // Compute each row.
          for (int j = 0; j < largest.Height; j++)
          {
             // Counts the number of bitmaps whose dimensions
             // contain the current location.
             int count = 0;
    
             // The sum of all alpha, red, green, and blue components.
             int a = 0, r = 0, g = 0, b = 0;
    
             // For each bitmap, compute the sum of all color components.
             foreach (var bitmapData in bitmapDataList)
             {
                // Ensure that we stay within the bounds of the image.
                if (bitmapData.Width > i && bitmapData.Height > j)
                {
                   unsafe
                   {
                      byte* row = (byte*)(bitmapData.Scan0 + (j * bitmapData.Stride));
                      byte* pix = (byte*)(row + (4 * i));
                      a += *pix; pix++;
                      r += *pix; pix++;
                      g += *pix; pix++;
                      b += *pix;
                   }
                   count++;
                }
             }
    
             //prevent divide by zero in bottom right pixelless corner
             if (count == 0)
                 break;
    
             unsafe
             {
                // Compute the average of each color component.
                a /= count;
                r /= count;
                g /= count;
                b /= count;
    
                // Set the result pixel.
                byte* row = (byte*)(resultBitmapData.Scan0 + (j * resultBitmapData.Stride));
                byte* pix = (byte*)(row + (4 * i));
                *pix = (byte)a; pix++;
                *pix = (byte)r; pix++;
                *pix = (byte)g; pix++;
                *pix = (byte)b;
             }
          }
       });
    
       // Unlock the source bitmaps.
       for (int i = 0; i < bitmapArray.Length; i++)
       {
          bitmapArray[i].UnlockBits(bitmapDataList[i]);
       }
    
       // Unlock the result bitmap.
       result.UnlockBits(resultBitmapData);
    
       // Return the result.
       return result;
    }
    

    Not

    Yöntemin CreateCompositeBitmap C# sürümü, nesnelerin verimli işlenmesini System.Drawing.Bitmap sağlamak için işaretçileri kullanır. Bu nedenle, güvenli olmayan anahtar sözcüğünü kullanmak için projenizde Güvenli olmayan koda izin ver seçeneğini etkinleştirmeniz gerekir. Visual C# projesinde güvenli olmayan kodu etkinleştirme hakkında daha fazla bilgi için bkz. Derleme Sayfası, Proje Tasarım Aracı (C#).

Aşağıdaki tabloda ağın üyeleri açıklanmaktadır.

Üye Tür Açıklama
loadBitmaps TransformBlock<TInput,TOutput> Giriş olarak bir klasör yolu alır ve çıkış olarak bir nesne koleksiyonu Bitmap oluşturur.
createCompositeBitmap TransformBlock<TInput,TOutput> Giriş olarak bir nesne koleksiyonu Bitmap alır ve çıkış olarak bileşik bir bit eşlem oluşturur.
displayCompositeBitmap ActionBlock<TInput> Formda bileşik bit eşlemi görüntüler.
operationCancelled ActionBlock<TInput> İşlemin iptal olduğunu belirten bir görüntü görüntüler ve kullanıcının başka bir klasör seçmesine olanak tanır.

Veri akışı bloklarını bir ağ oluşturmak üzere bağlamak için bu örnekte yöntemi kullanılır LinkTo . yöntemi, LinkTo hedef bloğun bir iletiyi kabul edip etmediğini belirleyen bir Predicate<T> nesne alan aşırı yüklenmiş bir sürüm içerir. Bu filtreleme mekanizması, ileti bloklarının yalnızca belirli değerleri almasını sağlar. Bu örnekte, ağ iki yoldan biriyle dallanabilir. Ana dal, diskten görüntüleri yükler, bileşik görüntüyü oluşturur ve bu görüntüyü formda görüntüler. Alternatif dal geçerli işlemi iptal eder. Nesneler, Predicate<T> ana dal boyunca veri akışı bloklarının belirli iletileri reddederek alternatif dala geçiş yapmasını sağlar. Örneğin, kullanıcı işlemi iptal ederse, veri akışı bloğu createCompositeBitmap çıkış olarak (NothingVisual Basic'te) üretir null . Veri akışı bloğu displayCompositeBitmap giriş değerlerini reddeder null ve bu nedenle iletisi öğesine operationCancelledsunulur. Veri akışı bloğu operationCancelled tüm iletileri kabul eder ve bu nedenle işlemin iptal olduğunu belirten bir görüntü görüntüler.

Aşağıdaki çizimde görüntü işleme ağı gösterilmektedir:

Görüntü işleme ağını gösteren çizim.

displayCompositeBitmap ve operationCancelled veri akışı blokları kullanıcı arabirimi üzerinde hareket ettiğinden, bu eylemlerin kullanıcı arabirimi iş parçacığında gerçekleşmesi önemlidir. Bunu gerçekleştirmek için, oluşturma sırasında bu nesnelerin her birinde özelliği olarak TaskScheduler.FromCurrentSynchronizationContextayarlanmış bir ExecutionDataflowBlockOptions nesne TaskScheduler sağlanır. yöntemi, TaskScheduler.FromCurrentSynchronizationContext geçerli eşitleme bağlamında iş gerçekleştiren bir TaskScheduler nesne oluşturur. CreateImageProcessingNetwork yöntemi, kullanıcı arabirimi iş parçacığında çalışan Klasör Seç düğmesinin işleyicisinden çağrıldığından, ve operationCancelled veri akışı blokları için displayCompositeBitmap eylemler de kullanıcı arabirimi iş parçacığında çalışır.

Özellik veri akışı bloğu yürütmeyi kalıcı olarak iptal ettiğinden CancellationTokenCancellationToken bu örnekte özelliği ayarlamak yerine paylaşılan bir iptal belirteci kullanılır. İptal belirteci, kullanıcı bir veya daha fazla işlemi iptal ettiğinde bile bu örneğin aynı veri akışı ağını birden çok kez yeniden kullanmasına olanak tanır. Veri akışı bloğunun yürütülmesini kalıcı olarak iptal etmek için kullanan CancellationToken bir örnek için bkz . Nasıl yapılır: Veri Akışı Bloğunu İptal Etme.

Veri Akışı Ağını Kullanıcı Arabirimine Bağlama

Bu bölümde veri akışı ağının kullanıcı arabirimine nasıl bağlandığı açıklanır. Bileşik görüntü oluşturma ve işlemin iptali , Klasör Seç ve İptal düğmelerinden başlatılır. Kullanıcı bu düğmelerden birini seçtiğinde, uygun eylem zaman uyumsuz bir şekilde başlatılır.

Veri Akışı Ağını Kullanıcı Arabirimine Bağlamak için

  1. Ana formun form tasarımcısında, Klasör Seç düğmesi için olay için Click bir olay işleyicisi oluşturun.

  2. ClickKlasör Seç düğmesi için olayı uygulayın.

    // Event handler for the Choose Folder button.
    private void toolStripButton1_Click(object sender, EventArgs e)
    {
       // Create a FolderBrowserDialog object to enable the user to
       // select a folder.
       FolderBrowserDialog dlg = new FolderBrowserDialog
       {
          ShowNewFolderButton = false
       };
    
       // Set the selected path to the common Sample Pictures folder
       // if it exists.
       string initialDirectory = Path.Combine(
          Environment.GetFolderPath(Environment.SpecialFolder.CommonPictures),
          "Sample Pictures");
       if (Directory.Exists(initialDirectory))
       {
          dlg.SelectedPath = initialDirectory;
       }
    
       // Show the dialog and process the dataflow network.
       if (dlg.ShowDialog() == DialogResult.OK)
       {
          // Create a new CancellationTokenSource object to enable
          // cancellation.
          cancellationTokenSource = new CancellationTokenSource();
    
          // Create the image processing network if needed.
          headBlock ??= CreateImageProcessingNetwork();
    
          // Post the selected path to the network.
          headBlock.Post(dlg.SelectedPath);
    
          // Enable the Cancel button and disable the Choose Folder button.
          toolStripButton1.Enabled = false;
          toolStripButton2.Enabled = true;
    
          // Show a wait cursor.
          Cursor = Cursors.WaitCursor;
       }
    }
    
  3. Ana formun form tasarımcısında İptal düğmesi için olay için Click bir olay işleyicisi oluşturun.

  4. Clickİptal düğmesi için olayı uygulayın.

    // Event handler for the Cancel button.
    private void toolStripButton2_Click(object sender, EventArgs e)
    {
       // Signal the request for cancellation. The current component of
       // the dataflow network will respond to the cancellation request.
       cancellationTokenSource.Cancel();
    }
    

Tam Örnek

Aşağıdaki örnekte bu izlenecek yol için tam kod gösterilmektedir.

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Threading.Tasks.Dataflow;
using System.Windows.Forms;

namespace CompositeImages
{
   public partial class Form1 : Form
   {
      // The head of the dataflow network.
      ITargetBlock<string> headBlock = null;

      // Enables the user interface to signal cancellation to the network.
      CancellationTokenSource cancellationTokenSource;

      public Form1()
      {
         InitializeComponent();
      }

      // Creates the image processing dataflow network and returns the
      // head node of the network.
      ITargetBlock<string> CreateImageProcessingNetwork()
      {
         //
         // Create the dataflow blocks that form the network.
         //

         // Create a dataflow block that takes a folder path as input
         // and returns a collection of Bitmap objects.
         var loadBitmaps = new TransformBlock<string, IEnumerable<Bitmap>>(path =>
            {
               try
               {
                  return LoadBitmaps(path);
               }
               catch (OperationCanceledException)
               {
                  // Handle cancellation by passing the empty collection
                  // to the next stage of the network.
                  return Enumerable.Empty<Bitmap>();
               }
            });

         // Create a dataflow block that takes a collection of Bitmap objects
         // and returns a single composite bitmap.
         var createCompositeBitmap = new TransformBlock<IEnumerable<Bitmap>, Bitmap>(bitmaps =>
            {
               try
               {
                  return CreateCompositeBitmap(bitmaps);
               }
               catch (OperationCanceledException)
               {
                  // Handle cancellation by passing null to the next stage
                  // of the network.
                  return null;
               }
            });

         // Create a dataflow block that displays the provided bitmap on the form.
         var displayCompositeBitmap = new ActionBlock<Bitmap>(bitmap =>
            {
               // Display the bitmap.
               pictureBox1.SizeMode = PictureBoxSizeMode.StretchImage;
               pictureBox1.Image = bitmap;

               // Enable the user to select another folder.
               toolStripButton1.Enabled = true;
               toolStripButton2.Enabled = false;
               Cursor = DefaultCursor;
            },
            // Specify a task scheduler from the current synchronization context
            // so that the action runs on the UI thread.
            new ExecutionDataflowBlockOptions
            {
                TaskScheduler = TaskScheduler.FromCurrentSynchronizationContext()
            });

         // Create a dataflow block that responds to a cancellation request by
         // displaying an image to indicate that the operation is cancelled and
         // enables the user to select another folder.
         var operationCancelled = new ActionBlock<object>(delegate
            {
               // Display the error image to indicate that the operation
               // was cancelled.
               pictureBox1.SizeMode = PictureBoxSizeMode.CenterImage;
               pictureBox1.Image = pictureBox1.ErrorImage;

               // Enable the user to select another folder.
               toolStripButton1.Enabled = true;
               toolStripButton2.Enabled = false;
               Cursor = DefaultCursor;
            },
            // Specify a task scheduler from the current synchronization context
            // so that the action runs on the UI thread.
            new ExecutionDataflowBlockOptions
            {
               TaskScheduler = TaskScheduler.FromCurrentSynchronizationContext()
            });

         //
         // Connect the network.
         //

         // Link loadBitmaps to createCompositeBitmap.
         // The provided predicate ensures that createCompositeBitmap accepts the
         // collection of bitmaps only if that collection has at least one member.
         loadBitmaps.LinkTo(createCompositeBitmap, bitmaps => bitmaps.Count() > 0);

         // Also link loadBitmaps to operationCancelled.
         // When createCompositeBitmap rejects the message, loadBitmaps
         // offers the message to operationCancelled.
         // operationCancelled accepts all messages because we do not provide a
         // predicate.
         loadBitmaps.LinkTo(operationCancelled);

         // Link createCompositeBitmap to displayCompositeBitmap.
         // The provided predicate ensures that displayCompositeBitmap accepts the
         // bitmap only if it is non-null.
         createCompositeBitmap.LinkTo(displayCompositeBitmap, bitmap => bitmap != null);

         // Also link createCompositeBitmap to operationCancelled.
         // When displayCompositeBitmap rejects the message, createCompositeBitmap
         // offers the message to operationCancelled.
         // operationCancelled accepts all messages because we do not provide a
         // predicate.
         createCompositeBitmap.LinkTo(operationCancelled);

         // Return the head of the network.
         return loadBitmaps;
      }

      // Loads all bitmap files that exist at the provided path.
      IEnumerable<Bitmap> LoadBitmaps(string path)
      {
         List<Bitmap> bitmaps = new List<Bitmap>();

         // Load a variety of image types.
         foreach (string bitmapType in
            new string[] { "*.bmp", "*.gif", "*.jpg", "*.png", "*.tif" })
         {
            // Load each bitmap for the current extension.
            foreach (string fileName in Directory.GetFiles(path, bitmapType))
            {
               // Throw OperationCanceledException if cancellation is requested.
               cancellationTokenSource.Token.ThrowIfCancellationRequested();

               try
               {
                  // Add the Bitmap object to the collection.
                  bitmaps.Add(new Bitmap(fileName));
               }
               catch (Exception)
               {
                  // TODO: A complete application might handle the error.
               }
            }
         }
         return bitmaps;
      }

      // Creates a composite bitmap from the provided collection of Bitmap objects.
      // This method computes the average color of each pixel among all bitmaps
      // to create the composite image.
      Bitmap CreateCompositeBitmap(IEnumerable<Bitmap> bitmaps)
      {
         Bitmap[] bitmapArray = bitmaps.ToArray();

         // Compute the maximum width and height components of all
         // bitmaps in the collection.
         Rectangle largest = new Rectangle();
         foreach (var bitmap in bitmapArray)
         {
            if (bitmap.Width > largest.Width)
               largest.Width = bitmap.Width;
            if (bitmap.Height > largest.Height)
               largest.Height = bitmap.Height;
         }

         // Create a 32-bit Bitmap object with the greatest dimensions.
         Bitmap result = new Bitmap(largest.Width, largest.Height,
            PixelFormat.Format32bppArgb);

         // Lock the result Bitmap.
         var resultBitmapData = result.LockBits(
            new Rectangle(new Point(), result.Size), ImageLockMode.WriteOnly,
            result.PixelFormat);

         // Lock each source bitmap to create a parallel list of BitmapData objects.
         var bitmapDataList = (from bitmap in bitmapArray
                               select bitmap.LockBits(
                                 new Rectangle(new Point(), bitmap.Size),
                                 ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb))
                              .ToList();

         // Compute each column in parallel.
         Parallel.For(0, largest.Width, new ParallelOptions
         {
            CancellationToken = cancellationTokenSource.Token
         },
         i =>
         {
            // Compute each row.
            for (int j = 0; j < largest.Height; j++)
            {
               // Counts the number of bitmaps whose dimensions
               // contain the current location.
               int count = 0;

               // The sum of all alpha, red, green, and blue components.
               int a = 0, r = 0, g = 0, b = 0;

               // For each bitmap, compute the sum of all color components.
               foreach (var bitmapData in bitmapDataList)
               {
                  // Ensure that we stay within the bounds of the image.
                  if (bitmapData.Width > i && bitmapData.Height > j)
                  {
                     unsafe
                     {
                        byte* row = (byte*)(bitmapData.Scan0 + (j * bitmapData.Stride));
                        byte* pix = (byte*)(row + (4 * i));
                        a += *pix; pix++;
                        r += *pix; pix++;
                        g += *pix; pix++;
                        b += *pix;
                     }
                     count++;
                  }
               }

               //prevent divide by zero in bottom right pixelless corner
               if (count == 0)
                   break;

               unsafe
               {
                  // Compute the average of each color component.
                  a /= count;
                  r /= count;
                  g /= count;
                  b /= count;

                  // Set the result pixel.
                  byte* row = (byte*)(resultBitmapData.Scan0 + (j * resultBitmapData.Stride));
                  byte* pix = (byte*)(row + (4 * i));
                  *pix = (byte)a; pix++;
                  *pix = (byte)r; pix++;
                  *pix = (byte)g; pix++;
                  *pix = (byte)b;
               }
            }
         });

         // Unlock the source bitmaps.
         for (int i = 0; i < bitmapArray.Length; i++)
         {
            bitmapArray[i].UnlockBits(bitmapDataList[i]);
         }

         // Unlock the result bitmap.
         result.UnlockBits(resultBitmapData);

         // Return the result.
         return result;
      }

      // Event handler for the Choose Folder button.
      private void toolStripButton1_Click(object sender, EventArgs e)
      {
         // Create a FolderBrowserDialog object to enable the user to
         // select a folder.
         FolderBrowserDialog dlg = new FolderBrowserDialog
         {
            ShowNewFolderButton = false
         };

         // Set the selected path to the common Sample Pictures folder
         // if it exists.
         string initialDirectory = Path.Combine(
            Environment.GetFolderPath(Environment.SpecialFolder.CommonPictures),
            "Sample Pictures");
         if (Directory.Exists(initialDirectory))
         {
            dlg.SelectedPath = initialDirectory;
         }

         // Show the dialog and process the dataflow network.
         if (dlg.ShowDialog() == DialogResult.OK)
         {
            // Create a new CancellationTokenSource object to enable
            // cancellation.
            cancellationTokenSource = new CancellationTokenSource();

            // Create the image processing network if needed.
            headBlock ??= CreateImageProcessingNetwork();

            // Post the selected path to the network.
            headBlock.Post(dlg.SelectedPath);

            // Enable the Cancel button and disable the Choose Folder button.
            toolStripButton1.Enabled = false;
            toolStripButton2.Enabled = true;

            // Show a wait cursor.
            Cursor = Cursors.WaitCursor;
         }
      }

      // Event handler for the Cancel button.
      private void toolStripButton2_Click(object sender, EventArgs e)
      {
         // Signal the request for cancellation. The current component of
         // the dataflow network will respond to the cancellation request.
         cancellationTokenSource.Cancel();
      }

      ~Form1()
      {
         cancellationTokenSource.Dispose();
      }
   }
}

Aşağıdaki çizimde ortak \Örnek Resimler\ klasörünün tipik çıkışı gösterilmektedir.

Windows Forms Uygulaması

Ayrıca bkz.