Date post: | 03-Sep-2014 |
Category: |
Technology |
Upload: | nick-pruehs |
View: | 1,269 times |
Download: | 0 times |
Tool DevelopmentChapter 07: Undo & Redo, Drag & Drop
Nick Prühs
June 25, 2014
5 Minute Review Session
• What are two possible approaches for binary serialization?
• What are the advantages and disadvantages of binary serialization via interfaces?
• What are the advantages and disadvantages of binary serialization via reflection?
• Why and when should you use a Background Worker in your UI?
• How do you work with Background Workers?
• How do you pass data to and from Background Workers?
2 / 58
Assignment Solution #6
DEMO
3 / 58
Objectives
• To understand how to implement an undo/redo stack
• To learn how to properly add drag & drop to your WPF application
4 / 58
Undo Sample Application
• Data• List of To-do items
• Operations• Add new To-do item
• Undo
• Redo
5 / 58
TodoItem Class
C#
6 / 58
public class TodoItem
{
public string ItemName { get; set; }
public TodoItem(string itemName)
{
this.ItemName = itemName;
}
}
MainWindow Markup
XAML
7 / 58
<Window x:Class="UndoDemo.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"Title="MainWindow" Height="350" Width="525">
<StackPanel><ListBox ItemsSource="{Binding Items}">
<ListBox.ItemTemplate><DataTemplate>
<TextBlock Text="{Binding ItemName}"/></DataTemplate>
</ListBox.ItemTemplate></ListBox><DockPanel>
<Button DockPanel.Dock="Right" Width="100" Click="OnRedo">Redo</Button><Button DockPanel.Dock="Right" Width="100" Click="OnUndo">Undo</Button><Button DockPanel.Dock="Right" Width="100" Click="OnAdd">Add</Button><TextBox DockPanel.Dock="Left" Name="TextBox"></TextBox>
</DockPanel></StackPanel>
</Window>
MainWindow Class
C#
8 / 58
private ObservableCollection<TodoItem> items = new ObservableCollection<TodoItem>();
public ObservableCollection<TodoItem> Items{
get{
return this.items;}
}
public MainWindow(){
InitializeComponent();
DataContext = this;}
private void OnAdd(object sender, RoutedEventArgs e){
string itemName = this.TextBox.Text;this.items.Add(new TodoItem(itemName));
}
Design Patterns
• General reusable solution to a commonly occurring problem within a given context
• Formalized best practices that the programmer must implement themselves in the application• Not a finished design that can be transformed directly
into source code
• Gained popularity in computer science after the book Design Patterns: Elements of Reusable Object-Oriented Software was published in 1994 by the so-called "Gang of Four" (Gamma et al.)
9 / 78
Advantages ofDesign Patterns• Speed up the development process by providing
tested, proven development paradigms
• Improve code readability for coders and architects who are familiar with the patterns
10 / 78
Design Pattern Types
• Creational (object creation)
• Structural (relationships between objects)
• Behavioral (communication between objects)
• Concurrency (multi-threaded programming)
11 / 78
Object-Oriented Design 101
• Aggregation• Combine simple objects or data types into more
complex ones
• Usually expressed by means of references from one object to another
• Inheritance• Adding detail to a general data type to create a more
specific data type
12 / 78
Object-Oriented Design 101
• Delegation• Handing a task over to another part of the program
• Polymorphism• Ad hoc polymorphism (function overloading)
• Parametric polymorphism (generic programming)
• Subtyping (subclassing)
13 / 78
Object-Oriented Design 101
• Cohesion• Degree to which the elements of a module belong
together
• How much functionalities embedded in a class have in common
• Coupling• Degree to which each program module relies on the
other modules
14 / 78
Object-Oriented Design 101
• Cohesion• Degree to which the elements of a module belong
together
• How much functionalities embedded in a class have in common
• Coupling• Degree to which each program module relies on the
other modules
15 / 78
Behavioral Design Patterns
Communication Between Objects:
• Iterator
• Observer
• Command
• Memento
• Strategy
16 / 78
Memento Pattern
Provides the ability to restore an object to its previous state.
17 / 78
Examples:
• Undo
Memento Pattern
Provides the ability to restore an object to its previous state.
18 / 78
Memento Class
C#
19 / 58
public class Memento
{
public ObservableCollection<TodoItem> Items { get; set; }
}
MainWindow Class
C#
20 / 58
private Stack<Memento> undoStack = new Stack<Memento>();
private Stack<Memento> redoStack = new Stack<Memento>();
private void SaveMemento()
{
Memento memento = new Memento { Items = new ObservableCollection<TodoItem>(this.items) };
this.undoStack.Push(memento);
}
private void RestoreFromMemento(Memento memento)
{
this.items.Clear();
foreach (var item in memento.Items)
{
this.items.Add(item);
}
}
MainWindow Class
C#
21 / 58
public MainWindow(){
InitializeComponent();
DataContext = this;
// Add initial (empty) state to undo stack.this.SaveMemento();
}
private void OnAdd(object sender, RoutedEventArgs e){
// Add new item.var itemName = this.TextBox.Text;this.items.Add(new TodoItem(itemName));
// Save memento and clear Redo stack.this.SaveMemento();this.redoStack.Clear();
}
MainWindow Class
C#
22 / 58
private void OnUndo(object sender, RoutedEventArgs e)
{
if (undoStack.Count < 2)
{
return;
}
// Move memento from Undo to Redo stack.
Memento memento = undoStack.Pop();
redoStack.Push(memento);
// Restore previous state.
memento = undoStack.Peek();
this.RestoreFromMemento(memento);
}
MainWindow Class
C#
23 / 58
private void OnRedo(object sender, RoutedEventArgs e)
{
if (redoStack.Count < 1)
{
return;
}
// Move memento from Redo to Undo stack.
Memento memento = redoStack.Pop();
undoStack.Push(memento);
// Restore previous state.
this.RestoreFromMemento(memento);
}
Command Pattern
Encapsulates all the information needed to call a method at a later time in an object.
24 / 78
Examples:
• Networking• Replays• AI• Undo
Command Pattern
Encapsulates all the information needed to call a method at a later time in an object.
25 / 78
Command Pattern
Encapsulates all the information needed to call a method at a later time in an object.
26 / 78
ICommand Interface
C#
27 / 58
public interface ICommand
{
void DoCommand();
void UndoCommand();
}
AddItemCommand Class
C#
28 / 58
public class AddItemCommand : ICommand{
private ObservableCollection<TodoItem> items;private string newItemName;
public AddItemCommand(ObservableCollection<TodoItem> items, string newItemName){this.items = items;this.newItemName = newItemName;
}
public void DoCommand(){TodoItem todoItem = new TodoItem(this.newItemName);this.items.Add(todoItem);
}
public void UndoCommand(){TodoItem todoItem = this.items.First(item => item.ItemName.Equals(this.newItemName));this.items.Remove(todoItem);
}}
MainWindow Class
C#
29 / 58
private Stack<ICommand> undoStack = new Stack<ICommand>();
private Stack<ICommand> redoStack = new Stack<ICommand>();
private void OnAdd(object sender, RoutedEventArgs e)
{
// Add new item.
var itemName = this.TextBox.Text;
AddItemCommand command = new AddItemCommand(this.items, itemName);
command.DoCommand();
// Save command and clear Redo stack.
this.undoStack.Push(command);
this.redoStack.Clear();
}
MainWindow Class
C#
30 / 58
private void OnUndo(object sender, RoutedEventArgs e)
{
if (undoStack.Count < 1)
{
return;
}
// Move command from Undo to Redo stack.
ICommand command = undoStack.Pop();
redoStack.Push(command);
// Undo command.
command.UndoCommand();
}
MainWindow Class
C#
31 / 58
private void OnRedo(object sender, RoutedEventArgs e)
{
if (redoStack.Count < 1)
{
return;
}
// Move command from Redo to Undo stack.
ICommand command = redoStack.Pop();
undoStack.Push(command);
// Redo command.
command.DoCommand();
}
Drag & Drop
• Commonly refers to a method of data transfer that involves using a mouse (or some other pointing device) to• select one or more objects,
• dragging these objects over some desired drop target in the user interface (UI),
• and dropping them.
32 / 58
Drag & Drop in WPF
• Drag-and-drop operations typically involve two parties:• drag source from which the dragged object originates
• drop target which receives the dropped object
• Drag source and drop target may be UI elements in the same application or a different application.
33 / 58
Drag & Drop in WPF
• The type and number of objects that can be manipulated with drag-and-drop is completely arbitrary.• Files
• Folders
• Content
• The particular actions performed during a drag-and-drop operation are application-specific, and often determined by context.
34 / 58
Drag & Drop Data Transfer
• Dragging and dropping items requires a way to temporarily store the transferred data.
• WPF uses a DataObject to store the data:• The drag source initiates a drag-and-drop operation,
passing the transferred data to it.• Any serializable object can be passed. If the data is not
already wrapped in a DataObject, it will automatically be wrapped in a new DataObject.
• For greater control over the data format, you can wrap the data in a DataObject yourself.
• The drop target is responsible for extracting the data from the DataObject.
35 / 58
Drag & Drop Effects
• Data that is actually being transferred typically does not have a visual representation.
• By default, feedback is provided to the user by changing the cursor to represent the effect that the drag-and-drop operation will have on the data, such as whether the data will be moved or copied.
• WPF defines a DragDropEffects enumeration that you can use to specify the effect of a drag-and-drop operation.
• It is important to remember that in WPF, the actual effect of the drag-and-drop operation depends on you to write the appropriate code in your application.• For example, the drop target might specify that the effect of
dropping data on it is to move the data.• However, to move the data, it must be both added to the target
element and removed from the source element.
36 / 58
Implementing Drag & Drop
1. Drop Source
1. Identify the element that will be a drag source. A drag source can be a UIElement or a ContentElement.
2. Create an event handler on the drag source that will initiate the drag-and-drop operation. The event is typically the MouseMove event.
3. In the drag source event handler, call the DoDragDrop method to initiate the drag-and-drop operation. In the DoDragDrop call, specify the drag source, the data to be transferred, and the allowed effects.
37 / 58
Implementing Drag & Drop
2. Drop Target
1. Identify the element that will be a drop target. A drop target can be UIElement or a ContentElement.
2. On the drop target, set the AllowDrop property to true.
3. In the drop target, create a Drop event handler to process the dropped data.
4. In the Drop event handler, extract the data from the DragEventArgs by using the GetDataPresent and GetData methods.
5. In the Drop event handler, use the data to perform the desired drag-and-drop operation.
38 / 58
Drag & DropSample Application• Data
• List of To-do items – Today
• List of To-do items – Tomorrow
• Operations• Move To-Do item from today to tomorrow
39 / 58
MainWindow Markup
XAML
40 / 58
<Window x:Class="DragDropDemo.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"Title="To-Do List" Height="350" Width="300">
<StackPanel Orientation="Horizontal"><StackPanel>
<TextBlock Text="Today" /><ListBox Width="150" ItemsSource="{Binding Today}" MouseMove="UIElement_OnMouseMove">
<ListBox.ItemTemplate><DataTemplate>
<TextBlock Text="{Binding ItemName}" /></DataTemplate>
</ListBox.ItemTemplate></ListBox>
</StackPanel><StackPanel>
<TextBlock Text="Tomorrow" /><ListBox Width="150" ItemsSource="{Binding Tomorrow}" AllowDrop="True"
DragOver="UIElement_OnDragOver" Drop="UIElement_OnDrop"><ListBox.ItemTemplate>
<DataTemplate><TextBlock Text="{Binding ItemName}"/>
</DataTemplate></ListBox.ItemTemplate>
</ListBox></StackPanel>
</StackPanel></Window>
MainWindow Class
C#
41 / 58
private ObservableCollection<TodoItem> today = new ObservableCollection<TodoItem>();private ObservableCollection<TodoItem> tomorrow = new ObservableCollection<TodoItem>();
public ObservableCollection<TodoItem> Today{
get { return this.today; }}
public ObservableCollection<TodoItem> Tomorrow{
get { return this.tomorrow; }}
public MainWindow(){
InitializeComponent();
DataContext = this;
this.today.Add(new TodoItem("Wash the dishes"));this.today.Add(new TodoItem("Clean up the closet"));this.tomorrow.Add(new TodoItem("Buy new pizza"));this.tomorrow.Add(new TodoItem("Prepare next lecture"));
}
MainWindow Class
C#
42 / 58
private void UIElement_OnMouseMove(object sender, MouseEventArgs e)
{
ListBox listBox = (ListBox)sender;
TodoItem selectedItem = (TodoItem)listBox.SelectedItem;
if (e.LeftButton == MouseButtonState.Pressed)
{
// Prepare data to be transferred.
DragDrop.DoDragDrop(listBox, selectedItem.ItemName, DragDropEffects.Move);
}
}
MainWindow Class
C#
43 / 58
private void UIElement_OnDragOver(object sender, DragEventArgs e){
e.Effects = DragDropEffects.None;
// Check if any data is present.if (e.Data.GetDataPresent(DataFormats.StringFormat)){
// Check if the data is valid for our to-do list.string dataString = (string)e.Data.GetData(DataFormats.StringFormat);
if (this.CheckData(dataString)){
// Change drag & drop effecte.Effects = DragDropEffects.Copy | DragDropEffects.Move;
}}
}
MainWindow Class
C#
44 / 58
private void UIElement_OnDrop(object sender, DragEventArgs e){
ListBox listBox = (ListBox)sender;
if (listBox != null){
// Check if any data is present.if (e.Data.GetDataPresent(DataFormats.StringFormat)){
// Check if the data is valid for our to-do list.string dataString = (string)e.Data.GetData(DataFormats.StringFormat);
if (this.CheckData(dataString)){
// Move to-do list item from today to tomorrow.TodoItem todoItem = today.First(item => item.ItemName.Equals(dataString));
today.Remove(todoItem);tomorrow.Add(todoItem);
}}
}}
Enhanced Drag & Drop
To transfer custom data or multiple data items, create a DataObject to pass to the DoDragDrop
method:
string stringData = "Some string data to store...";
string dataFormat = DataFormats.UnicodeText;
DataObject dataObject = new DataObject(dataFormat, stringData);
45 / 58
Drag & Drop Data Formats
46 / 58
Field Field Value Description
Bitmap "Bitmap" Specifies a Microsoft Windows bitmap data format.
CommaSeparatedValue "CSV" Specifies a comma-separated value (CSV) data format.
Dib "DeviceIndependentBitmap" Specifies the device-independent bitmap (DIB) data format.
Dif "DataInterchangeFormat" Specifies the Windows Data Interchange Format (DIF) data format.
EnhancedMetafile "EnhancedMetafile" Specifies the Windows enhanced metafile format.
FileDrop "FileDrop" Specifies the Windows file drop format.
Html "HTML Format" Specifies the HTML data format.
Locale "Locale" Specifies the Windows locale (culture) data format.
MetafilePicture "MetaFilePict" Specifies the Windows metafile picture data format.
OemText "OEMText" Specifies the standard Windows OEM text data format.
Drag & Drop Data Formats
47 / 58
Field Field Value Description
Palette "Palette" Specifies the Windows palette data format.
PenData "PenData" Specifies the Windows pen data format.
Riff "RiffAudio" Specifies the Resource Interchange File Format (RIFF) audio data format.
Rtf "Rich Text Format" Specifies the Rich Text Format (RTF) data format.
Serializable "PersistentObject" Specifies a data format that encapsulates any type of serializable data objects.
StringFormat "System.String" Specifies the common language runtime (CLR) string class data format.
SymbolicLink "SymbolicLink" Specifies the Windows symbolic link data format.
Text "Text" Specifies the ANSI text data format.
Tiff "TaggedImageFileFormat" Specifies the Tagged Image File Format (TIFF) data format.
UnicodeText "UnicodeText" Specifies the Unicode text data format.
WaveAudio "WaveAudio" Specifies the wave audio data format.
Xaml "Xaml" Specifies the Extensible Application Markup Language (XAML) data format.
XamlPackage "XamlPackage" Specifies the Extensible Application Markup Language (XAML) package data format.
Drag & Drop Preview
• DragEnter event is raised once each time an object is dragged into the bounds of an element that is acting as a drop target.• Handle this event to provide a preview of the effects of
the drag-and-drop operation, if appropriate for your application.
• DragLeave event occurs when the data is dragged out of the target's boundary without being dropped.• Handle this event to undo anything that you did in
the DragEnter event handler.
48 / 58
Drag & Drop Feedback
• GiveFeedback event is raised continuously while the drag source is being dragged.• Handle this event if you need to use custom cursors to
provide feedback to the user.
• Default handler for this event checks whether the drag source is over a valid drop target.
49 / 58
Cancel Drag & Drop
• QueryContinueDrag event is raised continuously while the drag source is being dragged.• Handle this event to determine what action ends the
drag-and-drop operation based on the state of the ESC, SHIFT, CTRL, and ALT keys, as well as the state of the mouse buttons.
• Default handler for this event cancels the drag-and-drop operation if the ESC key is pressed, and drops the data if the mouse button is released.
50 / 58
Assignment #7
Undo & Redo
1. Add menu items and toolbar buttons for Undo and Redo to your main window.
2. Use the Command pattern to add undo and redo for drawing map tiles to your application.
3. A single undo or redo should affect all map tiles drawn with a single click.
4. Both the undo and redo stacks should be cleared when creating a new map or loading an existing one.
5. Both operations should only be available if the respective stacks are not empty.
51 / 58
References
• Paul. Multilevel Undo and Redo Implementation in C#. http://www.codeproject.com/Articles/33384/Multilevel-Undo-and-Redo-Implementation-in-C-Part, February 17, 2009.
• MSDN. Drag and Drop Overview. http://msdn.microsoft.com/en-us/library/ms742859(v=vs.110).aspx, June 2014.
52 / 58
Thank you for your attention!
Contact
Blog
http://www.npruehs.de
@npruehs
Github
https://github.com/npruehs
53 / 58