Tải bản đầy đủ - 0 (trang)
17-21. Drag Items from a List and Drop Them on a Canvas

17-21. Drag Items from a List and Drop Them on a Canvas

Tải bản đầy đủ - 0trang


There is no single best way to do this, but usually you will need a combination of handling

MouseLeftButtonDown or PreviewMouseLeftButtonDown events to know when the user clicks something,

and MouseMove or PreviewMouseMove events to determine whether the user is moving the mouse while

holding the left button down. Also, you should use the SystemParameters.

MinimumHorizontalDragDistance and SystemParameters.MinimumVerticalDragDistance properties to

make sure the user has dragged the item a sufficient distance to be considered a drag operation;

otherwise, the user will often get false drag operations starting as they click items.

Once you are sure the user is trying to drag something, you configure the DragDrop object using the

DoDragDrop method. You must pass the DoDragDrop method a reference to the source object being

dragged, a System.Object containing the data that the drag operation is taking with it, and a value from

the System.Windows.DragDropEffects enumeration representing the type of drag operation being

performed. Commonly used values of the DragDropEffects enumeration are Copy, Move, and Link. The

type of operation is often driven by special keys being held down at the time of clicking—for example,

holding the Ctrl key signals the user’s intent to copy (see recipe 17-34 for information on how to query

keyboard state).

On the target of the drop operation, implement event handlers for the DragEnter and Drop events.

The DragEnter handler allows you to control the behavior seen by the user as the mouse pointer enters

the target control. This usually indicates whether the control is a suitable target for the type of content

the user is dragging. The Drop event signals that the user has released the left mouse button and

indicates that the content contained in the DragDrop object should be retrieved (using the Data.GetData

method of the DragEventArgs object passed to the Drop event handler) and inserted into the target


The Code

The following XAML demonstrates how to set up a ListBox with ListBoxItem objects that support dragand-drop operations (see Figure 17-19):



Title="Recipe17_21" Height="300" Width="300">












DragEnter="cvsSurface_DragEnter" Drop="cvsSurface_Drop"

Name="cvsSurface" >

The following code-behind contains the event handlers that allow the example to identify the

ListBoxItem that the user is dragging, determine whether a mouse movement constitutes a drag

operation, and allow the Canvas to receive the dragged ListBoxItem content.









namespace Apress.VisualCSharpRecipes.Chapter17



/// Interaction logic for MainWindow.xaml


public partial class MainWindow : Window


private ListBoxItem draggedItem;

private Point startDragPoint;

public MainWindow()




// Handles the DragEnter event for the Canvas. Changes the mouse

// pointer to show the user that copy is an option if the drop

// text content is over the Canvas.

private void cvsSurface_DragEnter(object sender, DragEventArgs e)


if (e.Data.GetDataPresent(DataFormats.Text))


e.Effects = DragDropEffects.Copy;




e.Effects = DragDropEffects.None;



// Handles the Drop event for the Canvas. Creates a new Label

// and adds it to the Canvas at the location of the mouse pointer.




private void cvsSurface_Drop(object sender, DragEventArgs e)


// Create a new Label.

Label newLabel = new Label();

newLabel.Content = e.Data.GetData(DataFormats.Text);

newLabel.FontSize = 14;

// Add the Label to the Canvas and position it.


Canvas.SetLeft(newLabel, e.GetPosition(cvsSurface).X);

Canvas.SetTop(newLabel, e.GetPosition(cvsSurface).Y);


// Handles the PreviewMouseLeftButtonDown event for all ListBoxItem

// objects. Stores a reference to the item being dragged and the

// point at which the drag started.

private void ListBoxItem_PreviewMouseLeftButtonDown(object sender,

MouseButtonEventArgs e)


draggedItem = sender as ListBoxItem;

startDragPoint = e.GetPosition(null);


// Handles the PreviewMouseMove event for all ListBoxItem objects.

// Determines whether the mouse has been moved far enough to be

// considered a drag operation.

private void ListBoxItem_PreviewMouseMove(object sender,

MouseEventArgs e)


if (e.LeftButton == MouseButtonState.Pressed)


Point position = e.GetPosition(null);

if (Math.Abs(position.X - startDragPoint.X) >

SystemParameters.MinimumHorizontalDragDistance ||

Math.Abs(position.Y - startDragPoint.Y) >



// User is dragging, set up the DragDrop behavior.

DragDrop.DoDragDrop(draggedItem, draggedItem.Content,










Figure 17-19. Dragging items from a ListBox and dropping them on a Canvas

17-22. Display the Progress of a Long-Running Operation and

Allow the User to Cancel It


You need to execute a method asynchronously on a background thread, show a System.Windows.

Controls.ProgressBar while the process is executing, and allow the user to cancel the background

operation before completion.


Create an instance of the System.ComponentModel.BackgroundWorker class and attach event handlers to

its DoWork and RunWorkerCompleted events. To report progress, set its WorkerReportsProgress property to

True, and add an event handler to its ProgressChanged event. Call the ReportProgress method of the

BackgroundWorker while processing the operation on the background thread, and in the code for this

ProgressChanged event handler, update the Value property of a ProgressBar.

To support cancellation, set its WorkerSupportsCancellation property to True and call the

CancelAsync method when the user wants to cancel the operation. In the DoWork event handler,

check the CancellationPending property, and if this is True, use the Cancel property of System.

ComponentModel.DoWorkEventArgs to notify the RunWorkerCompleted event handler that the operation

was cancelled.




How It Works

The BackgroundWorker component gives you the ability to execute time-consuming operations

asynchronously. It automatically executes the operation on a different thread to the one that created it

and then automatically returns control to the calling thread when it is completed.

The BackgroundWorker’s DoWork event specifies the delegate to execute asynchronously. It is this

delegate that is executed on a background thread when the RunWorkerAsync method is called. When it

has completed the operation, it calls the RunWorkerCompleted event and executes the attached delegate

on the same thread that was used to create it. If the BackgroundWorker object is created on the UI

thread—for example, in the constructor method for a window or control—then you can access and

update the UI in the RunWorkerCompleted event without having to check that you are on the UI thread

again. The BackgroundWorker object handles all the thread marshaling for you.

The DoWork method takes an argument of type System.ComponentModel.DoWorkEventArgs, which

allows you to pass an argument to the method. The RunWorkerCompleted event is passed an instance of

the System.ComponentModel.RunWorkerCompletedEventArgs class, which allows you to receive the result of

the background process and any error that might have been thrown during processing.

The BackgroundWorker class has a Boolean property called WorkerReportsProgress, which indicates

whether the BackgroundWorker can report progress updates. It is set to False by default. When this is set

to True, calling the ReportProgress method will raise the ProgressChanged event. The ReportProgress

method takes an integer parameter specifying the percentage of progress completed by the

BackgroundWorker. This parameter is passed to the ProgressChanged event handler via the

ProgressPercentage property of the System.ComponentModel.ProgressChangedEventArgs class. The

ProgressBar control sets the default value for its Maximum property to 100, which lends itself perfectly and

automatically to receive the ProgressPercentage as its Value property.

The BackgroundWorker class has a Boolean property called WorkerSupportsCancellation, which when

set to True allows the CancelAsync method to interrupt the background operation. It is set to False by

default. In the RunWorkerCompleted event handler, you can use the Cancelled property of the

RunWorkerCompletedEventArgs to check whether the BackgroundWorker was cancelled.

The Code

The following example demonstrates a window that declares a ProgressBar control and a Button. An

instance of the BackgroundWorker class is created in the window’s constructor, and its

WorkerSupportsCancellation property is set to True.

When the Button is clicked, the code in the Click handler runs the BackgroundWorker

asynchronously and changes the text of the Button from Start to Cancel. If it is clicked again, the IsBusy

property of the BackgroundWorker returns True, and the code calls the CancelAsync method to cancel the


In the RunWorkerCompleted event handler, a System.Windows.MessageBox is shown if the Cancelled

property of the RunWorkerCompletedEventArgs parameter is True. The XAML for the window is as follows:



Title="Recipe17_22" Height="100" Width="250">



Tài liệu bạn tìm kiếm đã sẵn sàng tải về

17-21. Drag Items from a List and Drop Them on a Canvas

Tải bản đầy đủ ngay(0 tr)