Návod: Použití toku dat ve formulářové aplikaci Windows
Tento článek ukazuje, jak vytvořit síť bloků toku dat, které provádějí zpracování obrázků v aplikaci model Windows Forms.
Tento příklad načte soubory obrázků ze zadané složky, vytvoří složený obrázek a zobrazí výsledek. V příkladu se ke směrování obrázků přes síť používá model toku dat. V modelu toku dat spolu nezávislé komponenty programu komunikují prostřednictvím odesílání zpráv. Když komponenta obdrží zprávu, provede určitou akci a výsledek pak předá jiné komponentě. Porovnejte to s modelem toku řízení, ve kterém aplikace používá řídicí struktury, například podmíněné příkazy, smyčky atd., k řízení pořadí operací v programu.
Požadavky
Než začnete s tímto návodem, přečtěte si téma Tok dat.
Poznámka
Knihovna toků dat TPL (obor názvů) se s .NET nedistribuuje System.Threading.Tasks.Dataflow . Pokud chcete nainstalovat System.Threading.Tasks.Dataflow obor názvů v sadě Visual Studio, otevřete projekt, v nabídce Projekt zvolte Spravovat balíčky NuGet a vyhledejte System.Threading.Tasks.Dataflow
balíček online. Pokud ho chcete nainstalovat pomocí rozhraní příkazového řádku .NET Core, spusťte příkaz dotnet add package System.Threading.Tasks.Dataflow
.
Oddíly
Tento návod obsahuje následující části:
Vytvoření aplikace model Windows Forms
Tato část popisuje, jak vytvořit základní model Windows Forms aplikaci a přidat ovládací prvky do hlavního formuláře.
Vytvoření aplikace model Windows Forms
V sadě Visual Studio vytvořte projekt Visual C# nebo Visual Basic model Windows Forms Application. V tomto dokumentu má projekt název
CompositeImages
.V návrháři formuláře pro hlavní formulář Form1.cs (Form1.vb pro Visual Basic) přidejte ovládací prvek ToolStrip .
Přidejte do ToolStripButton ovládacího ToolStrip prvku ovládací prvek. DisplayStyle Nastavte vlastnost na Text a vlastnost Text na Zvolte složku.
Přidejte do ToolStrip ovládacího prvku druhý ToolStripButton ovládací prvek. DisplayStyle Nastavte vlastnost na Text, vlastnost Text na Zrušit a Enabled vlastnost na
False
.PictureBox Přidejte objekt do hlavního formuláře. Nastavte Dock vlastnost na Fill.
Vytvoření sítě toků dat
Tato část popisuje, jak vytvořit síť toku dat, která provádí zpracování obrázků.
Vytvoření sítě toků dat
Přidejte do projektu odkaz na System.Threading.Tasks.Dataflow.dll.
Ujistěte se, že Form1.cs (Form1.vb pro Visual Basic) obsahuje následující
using
příkazy (Using
v jazyce Visual Basic):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;
Do třídy přidejte následující datové členy
Form1
:// The head of the dataflow network. ITargetBlock<string> headBlock = null; // Enables the user interface to signal cancellation to the network. CancellationTokenSource cancellationTokenSource;
Do třídy přidejte následující metodu
CreateImageProcessingNetwork
Form1
. Tato metoda vytvoří síť pro zpracování obrázků.// 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; }
Implementujte metodu
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; }
Implementujte metodu
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; }
Poznámka
Verze C#
CreateCompositeBitmap
metody používá ukazatele k zajištění efektivního System.Drawing.Bitmap zpracování objektů. Proto musíte povolit možnost Povolit nebezpečný kód v projektu, aby bylo možné použít nebezpečné klíčové slovo. Další informace o povolení nebezpečného kódu v projektu Visual C# najdete v tématu Stránka sestavení, Projekt Designer (C#).
Následující tabulka popisuje členy sítě.
Člen | Typ | Description |
---|---|---|
loadBitmaps |
TransformBlock<TInput,TOutput> | Vezme cestu ke složce jako vstup a vytvoří kolekci Bitmap objektů jako výstup. |
createCompositeBitmap |
TransformBlock<TInput,TOutput> | Vezme kolekci Bitmap objektů jako vstup a vytvoří složený rastrový obrázek jako výstup. |
displayCompositeBitmap |
ActionBlock<TInput> | Zobrazí složený rastrový obrázek ve formuláři. |
operationCancelled |
ActionBlock<TInput> | Zobrazí obrázek, který označuje, že operace je zrušena, a umožní uživateli vybrat jinou složku. |
K připojení bloků toku dat k vytvoření sítě se v tomto příkladu LinkTo používá metoda . Metoda LinkTo obsahuje přetíženou verzi, která přebírá Predicate<T> objekt, který určuje, zda cílový blok přijme nebo odmítne zprávu. Tento filtrovací mechanismus umožňuje, aby bloky zpráv přijímaly pouze určité hodnoty. V tomto příkladu se síť může rozvětvovat jedním ze dvou způsobů. Hlavní větev načte image z disku, vytvoří složenou image a zobrazí ji ve formuláři. Alternativní větev zruší aktuální operaci. Objekty Predicate<T> umožňují blokům toku dat podél hlavní větve přepnout na alternativní větev odmítnutím určitých zpráv. Pokud například uživatel zruší operaci, vytvoří blok createCompositeBitmap
null
toku dat výstup (Nothing
v jazyce Visual Basic). Blok displayCompositeBitmap
toku dat odmítne null
vstupní hodnoty, a proto se zpráva nabídne službě operationCancelled
. Blok operationCancelled
toku dat přijímá všechny zprávy, a proto zobrazí obrázek, který označuje, že operace byla zrušena.
Následující obrázek znázorňuje síť pro zpracování obrázků:
Vzhledem k tomu, displayCompositeBitmap
že bloky toku dat a operationCancelled
fungují na uživatelském rozhraní, je důležité, aby tyto akce probíhaly ve vlákně uživatelského rozhraní. Aby toho bylo možné dosáhnout, během výstavby každý z těchto objektů poskytuje ExecutionDataflowBlockOptions objekt, který má vlastnost nastavenou TaskScheduler na TaskScheduler.FromCurrentSynchronizationContext. Metoda TaskScheduler.FromCurrentSynchronizationContext vytvoří TaskScheduler objekt, který provádí práci s aktuálním kontextem synchronizace. Vzhledem k tomu, že CreateImageProcessingNetwork
metoda je volána z obslužné rutiny tlačítka Zvolit složku , které běží na vlákně uživatelského rozhraní, akce pro displayCompositeBitmap
bloky toku dat a operationCancelled
také spustit ve vlákně uživatelského rozhraní.
V tomto příkladu se místo nastavení CancellationToken vlastnosti používá sdílený token zrušení, protože CancellationToken vlastnost trvale zruší provádění bloku toku dat. Token zrušení umožňuje v tomto příkladu opakovaně používat stejnou síť toku dat, a to i v případě, že uživatel zruší jednu nebo více operací. Příklad, který používá CancellationToken k trvalému zrušení spuštění bloku toku dat, najdete v tématu Postupy: Zrušení bloku toku dat.
Připojení sítě toků dat k uživatelskému rozhraní
Tato část popisuje, jak připojit síť toku dat k uživatelskému rozhraní. Vytvoření složeného obrázku a zrušení operace se zahájí pomocí tlačítek Zvolit složku a Zrušit . Když uživatel zvolí některé z těchto tlačítek, spustí se příslušná akce asynchronním způsobem.
Připojení sítě toků dat k uživatelskému rozhraní
V návrháři formuláře pro hlavní formulář vytvořte obslužnou rutinu Click události pro událost pro tlačítko Zvolit složku .
Click Implementujte událost pro tlačítko Zvolit složku.
// 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; } }
V návrháři formuláře pro hlavní formulář vytvořte obslužnou rutinu Click události pro událost tlačítka Zrušit .
Click Implementujte událost tlačítka Zrušit.
// 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(); }
Kompletní příklad
Následující příklad ukazuje úplný kód pro tento názorný postup.
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();
}
}
}
Následující obrázek znázorňuje typický výstup pro společnou složku \Sample Pictures\.