Megosztás a következőn keresztül:


Útmutató: Adatfolyam használata Windows Forms alkalmazásban

Ez a cikk bemutatja, hogyan hozhat létre adatfolyamblokkokból álló hálózatot, amely képfeldolgozást végez egy Windows Forms alkalmazásban.

Ez a példa betölti a képfájlokat a megadott mappából, létrehoz egy összetett képet, és megjeleníti az eredményt. A példa az adatfolyam-modellel irányítja át a képeket a hálózaton. Az adatfolyam-modellben a program független összetevői üzenetek küldésével kommunikálnak egymással. Amikor egy összetevő üzenetet kap, végrehajt egy műveletet, majd átadja az eredményt egy másik összetevőnek. Hasonlítsa össze ezt a vezérlőfolyamat-modellel, amelyben az alkalmazások vezérlőstruktúrákat, például feltételes utasításokat, hurkokat stb. használnak a programok műveleteinek sorrendjének szabályozásához.

Előfeltételek

Mielőtt elkezdené ezt az útmutatót, olvassa el az Adatfolyam elemet.

Megjegyzés

A TPL-adatfolyamtár (a System.Threading.Tasks.Dataflow névtér) nincs elosztva a .NET-tel. Ha telepíteni szeretné a névteret a System.Threading.Tasks.Dataflow Visual Studióban, nyissa meg a projektet, válassza a Project menü NuGet-csomagok kezelése elemét, és keressen rá online a System.Threading.Tasks.Dataflow csomagra. Másik lehetőségként futtassa dotnet add package System.Threading.Tasks.Dataflowa parancsot a .NET Core parancssori felülettel történő telepítéséhez.

Szakaszok

Ez az útmutató a következő szakaszokat tartalmazza:

A Windows Forms alkalmazás létrehozása

Ez a szakasz bemutatja, hogyan hozhatja létre az alapszintű Windows Forms alkalmazást, és hogyan adhat hozzá vezérlőket a főűrlaphoz.

A Windows Forms alkalmazás létrehozása

  1. A Visual Studióban hozzon létre egy Visual C# vagy Visual Basic Windows Forms Application projektet. Ebben a dokumentumban a projekt neve CompositeImages.

  2. A főűrlap Űrlap1.cs (Visual Basichez Form1.vb) űrlaptervezőjében adjon hozzá egy vezérlőt ToolStrip .

  3. Adjon hozzá egy ToolStripButton vezérlőt a ToolStrip vezérlőhöz. Állítsa a tulajdonságot értékre DisplayStyleText , a Text tulajdonságot pedig a Mappa kiválasztása értékre.

  4. Adjon hozzá egy második ToolStripButton vezérlőt a ToolStrip vezérlőhöz. Állítsa a tulajdonságot értékre DisplayStyleText, a Text tulajdonságot Mégse értékre, a Enabled tulajdonságot pedig értékre False.

  5. PictureBox Objektum hozzáadása a főűrlaphoz. Állítsa a tulajdonságot értékre DockFill.

Az adatfolyam-hálózat létrehozása

Ez a szakasz a képfeldolgozást végző adatfolyam-hálózat létrehozását ismerteti.

Az adatfolyam-hálózat létrehozása

  1. Adjon hozzá egy hivatkozást System.Threading.Tasks.Dataflow.dll a projekthez.

  2. Győződjön meg arról, hogy a Form1.cs (Form1.vb for Visual Basic) tartalmazza a következő using (Using Visual Basic)-utasításokat:

    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. Adja hozzá a következő adattagokat az Form1 osztályhoz:

    // The head of the dataflow network.
    ITargetBlock<string> headBlock = null;
    
    // Enables the user interface to signal cancellation to the network.
    CancellationTokenSource cancellationTokenSource;
    
  4. Adja hozzá a következő metódust a CreateImageProcessingNetworkosztályhoz Form1 . Ez a metódus létrehozza a képfeldolgozó hálózatot.

    // 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. Implementálja a metódust 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;
    }
    
  6. Implementálja a metódust CreateCompositeBitmap .

    // 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;
    }
    

    Megjegyzés

    A metódus C#-verziója CreateCompositeBitmap mutatókkal teszi lehetővé az System.Drawing.Bitmap objektumok hatékony feldolgozását. Ezért a nem biztonságos kulcsszó használatához engedélyeznie kell a nem biztonságos kód engedélyezése beállítást a projektben. A nem biztonságos kód Visual C#-projektekben való engedélyezéséről további információt a Build Page, Project Tervező (C#) című témakörben talál.

Az alábbi táblázat a hálózat tagjait ismerteti.

Tag Típus Leírás
loadBitmaps TransformBlock<TInput,TOutput> Bemenetként egy mappaelérési Bitmap utat vesz fel, és kimenetként objektumgyűjteményt hoz létre.
createCompositeBitmap TransformBlock<TInput,TOutput> Bemenetként egy objektumgyűjteményt Bitmap vesz fel, és kimenetként összetett bitképet hoz létre.
displayCompositeBitmap ActionBlock<TInput> Megjeleníti az összetett bitképet az űrlapon.
operationCancelled ActionBlock<TInput> Egy képet jelenít meg, amely jelzi, hogy a művelet megszakadt, és lehetővé teszi a felhasználó számára egy másik mappa kiválasztását.

Az adatfolyamblokkok hálózat létrehozásához való csatlakoztatásához ez a példa a metódust LinkTo használja. A LinkTo metódus egy túlterhelt verziót tartalmaz, amely egy Predicate<T> objektumot fogad el, amely meghatározza, hogy a célblokk elfogad-e vagy elutasít egy üzenetet. Ez a szűrési mechanizmus lehetővé teszi, hogy az üzenetblokkok csak bizonyos értékeket fogadjanak. Ebben a példában a hálózat kétféleképpen ágazhat el. A főág betölti a lemezképeket, létrehozza az összetett képet, és megjeleníti a képet az űrlapon. A másodlagos ág megszakítja az aktuális műveletet. Az Predicate<T> objektumok lehetővé teszik, hogy az adatfolyam-blokkok a főág mentén válthassanak az alternatív ágra bizonyos üzenetek elutasításával. Ha például a felhasználó megszakítja a műveletet, az adatfolyam-blokk createCompositeBitmap kimenete null (Nothing Visual Basicben) lesz. Az adatfolyam-blokk displayCompositeBitmap elutasítja a null bemeneti értékeket, ezért a rendszer felajánlja az üzenetet a számára operationCancelled. Az adatfolyam-blokk operationCancelled minden üzenetet elfogad, ezért megjelenít egy képet, amely jelzi a művelet megszakítását.

Az alábbi ábrán a képfeldolgozó hálózat látható:

A képfeldolgozó hálózatot bemutató ábra.

Mivel a és operationCancelled az displayCompositeBitmap adatfolyam blokkok a felhasználói felületen működnek, fontos, hogy ezek a műveletek a felhasználói felület szálán történjenek. Ennek érdekében az építés során ezek az objektumok egy objektumot ExecutionDataflowBlockOptions biztosítanak, amelynek TaskScheduler tulajdonsága értékre TaskScheduler.FromCurrentSynchronizationContextvan állítva. A TaskScheduler.FromCurrentSynchronizationContext metódus létrehoz egy TaskScheduler objektumot, amely az aktuális szinkronizálási környezetben végez munkát. Mivel a metódus a CreateImageProcessingNetwork felhasználói felületi szálon futó Mappa kiválasztása gomb kezelőjéből van meghívva, a és operationCancelled adatfolyam-blokkok műveletei displayCompositeBitmap is a felhasználói felület szálán futnak.

Ez a példa megosztott lemondási jogkivonatot használ a CancellationToken tulajdonság beállítása helyett, mivel a tulajdonság véglegesen megszakítja az CancellationToken adatfolyamblokkok végrehajtását. A lemondási jogkivonat lehetővé teszi, hogy ez a példa többször is újra felhasználja ugyanazt az adatfolyam-hálózatot, még akkor is, ha a felhasználó megszakít egy vagy több műveletet. Egy adatfolyam-blokk végrehajtásának végleges megszakítására szolgáló CancellationToken példa : How to: Cancel a Dataflow Block (Adatfolyam-blokk megszakítása).

Az adatfolyam-hálózat csatlakoztatása a felhasználói felülethez

Ez a szakasz azt ismerteti, hogyan csatlakoztathatja az adatfolyam-hálózatot a felhasználói felülethez. Az összetett rendszerkép létrehozását és a művelet megszakítását a Mappa kiválasztása és a Mégse gomb indítja el. Ha a felhasználó ezen gombok bármelyikét választja, a megfelelő műveletet aszinkron módon indítja el a rendszer.

Az adatfolyam-hálózat csatlakoztatása a felhasználói felülethez

  1. A főűrlap űrlaptervezőjében hozzon létre egy eseménykezelőt az Click eseményhez a Mappa kiválasztása gombhoz.

  2. Implementálja a ClickMappa kiválasztása gomb eseményét.

    // 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. A főűrlap űrlaptervezőjében hozzon létre egy eseménykezelőt az Click eseményhez a Mégse gombhoz.

  4. Implementálja a ClickMégse gomb eseményét.

    // 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();
    }
    

A teljes példa

Az alábbi példa az útmutató teljes kódját mutatja be.

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();
      }
   }
}

Az alábbi ábrán a gyakori \Sample Pictures\ mappa jellemző kimenete látható.

A Windows Forms alkalmazás

Lásd még