using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
using Tango.Core.Commands;
using Tango.Editors;
namespace Tango.Editors
{
///
/// Represents a collection container and editor. The editor supports Undo, Redo, Cut, Copy and Paste operations.
///
///
///
///
///
public partial class ElementsEditor : HybridControl, IConfigurable, ISupportEditingOperations, ISupportUndoRedoOperations
{
private List _copiedElements;
private bool _isSelectionMouseDown; //Determines whether the mouse is down for selection by the selection rectangle.
private Point _selectionMouseDownPoint; //Holds the originating point to perform the selection.
private bool _selectionPerformed; //Determines whether the selection rectangle has moved at least 1 pixel.
#region Events
///
/// Occurs when attempting to create a new element using the selection rectangle and holding the shift key.
///
public event EventHandler ElementCreation;
///
/// Occurs before removing selected elements using the DELETE key.
///
public event EventHandler RemovingElements;
///
/// Occurs when elements selection has changed;.
///
public event EventHandler SelectionChanged;
///
/// Occurs after pasting the currently copied elements. The collection of elements represents the new cloned elements.
///
public event EventHandler AfterPaste;
///
/// Occurs when one or many elements were added.
///
public event EventHandler ElementsAdded;
///
/// Occurs when one or many elements were removed.
///
public event EventHandler ElementsRemoved;
public event EventHandler ElementDoubleClicked;
#endregion
#region Constructors
///
/// Initializes a new instance of the class.
///
public ElementsEditor()
{
//Initialize Collections
//Elements = new ObservableCollection();
SelectedElements = new ObservableCollection();
InitializeComponent();
//Initialize Default Undo/Redo States Provider.
UndoRedoStatesProvider = new ElementsEditorUndoRedoStatesProvider(this);
//Register Events.
Loaded += ElementsEditor_Loaded;
//Initialize Commands
CopyCommand = new RelayCommand(Copy, (x) => GetSelectedElements().Count > 0);
PasteCommand = new RelayCommand(Paste, (x) => _copiedElements != null && _copiedElements.Count > 0);
UndoCommand = new RelayCommand(Undo, (x) => UndoRedoStatesProvider.CanUndo);
RedoCommand = new RelayCommand(Redo, (x) => UndoRedoStatesProvider.CanRedo);
DeleteCommand = new RelayCommand(() => { RemoveSelectedElements(true); }, (x) => GetSelectedElements().Count > 0);
CutCommand = new RelayCommand(Cut, (x) => GetSelectedElements().Count > 0);
ZoomCommand = new RelayCommand((str) => { Zoom(Convert.ToDouble(str)); });
ResetZoomCommand = new RelayCommand(() => { ScaleFactor = 1; });
SelectAllCommand = new RelayCommand(SelectAll);
BringToFrontCommand = new RelayCommand(BringToFront, (x) => GetSelectedElements().Count > 0);
SendToBackCommand = new RelayCommand(SendToBack, (x) => GetSelectedElements().Count > 0);
}
#endregion
#region Properties
public BitmapSource PreviewImage
{
get { return (BitmapSource)GetValue(PreviewImageProperty); }
set { SetValue(PreviewImageProperty, value); }
}
public static readonly DependencyProperty PreviewImageProperty =
DependencyProperty.Register("PreviewImage", typeof(BitmapSource), typeof(ElementsEditor), new PropertyMetadata(null));
///
/// Gets or sets a value indicating whether to enable undo and redo operations using the keyboard.
///
public bool EnableKeyboardUndoRedoOperations
{
get { return (bool)GetValue(EnableKeyboardUndoRedoOperationsProperty); }
set { SetValue(EnableKeyboardUndoRedoOperationsProperty, value); }
}
public static readonly DependencyProperty EnableKeyboardUndoRedoOperationsProperty =
DependencyProperty.Register("EnableKeyboardUndoRedoOperations", typeof(bool), typeof(ElementsEditor), new PropertyMetadata(true));
///
/// Gets or sets a value indicating whether to enable editing operations using the keyboard.
///
public bool EnableKeyboardEditingOperations
{
get { return (bool)GetValue(EnableKeyboardEditingOperationsProperty); }
set { SetValue(EnableKeyboardEditingOperationsProperty, value); }
}
public static readonly DependencyProperty EnableKeyboardEditingOperationsProperty =
DependencyProperty.Register("EnableKeyboardEditingOperations", typeof(bool), typeof(ElementsEditor), new PropertyMetadata(true));
///
/// Gets or sets the width of the editor.
///
public double EditorWidth
{
get { return (double)GetValue(EditorWidthProperty); }
set { SetValue(EditorWidthProperty, value); }
}
public static readonly DependencyProperty EditorWidthProperty =
DependencyProperty.Register("EditorWidth", typeof(double), typeof(ElementsEditor), new PropertyMetadata(1280.0));
///
/// Gets or sets the height of the editor.
///
public double EditorHeight
{
get { return (double)GetValue(EditorHeightProperty); }
set { SetValue(EditorHeightProperty, value); }
}
public static readonly DependencyProperty EditorHeightProperty =
DependencyProperty.Register("EditorHeight", typeof(double), typeof(ElementsEditor), new PropertyMetadata(720.0));
///
/// Gets or sets the editor scale factor.
///
public double ScaleFactor
{
get { return (double)GetValue(ScaleFactorProperty); }
set { SetValue(ScaleFactorProperty, value); }
}
public static readonly DependencyProperty ScaleFactorProperty =
DependencyProperty.Register("ScaleFactor", typeof(double), typeof(ElementsEditor), new PropertyMetadata(1.0, null, (d, e) => { return (d as ElementsEditor).OnCoerceScaleFactor(e); }));
///
/// Gets or sets the collection of .
///
public ObservableCollection Elements
{
get { return (ObservableCollection)GetValue(ElementsProperty); }
set { SetValue(ElementsProperty, value); }
}
public static readonly DependencyProperty ElementsProperty =
DependencyProperty.Register("Elements", typeof(ObservableCollection), typeof(ElementsEditor), new PropertyMetadata(null, (d, e) => { (d as ElementsEditor).OnElementsChanged(); }));
///
/// Gets or sets the selected element.
///
public IElementEditor SelectedElement
{
get { return (IElementEditor)GetValue(SelectedElementProperty); }
set { SetValue(SelectedElementProperty, value); }
}
public static readonly DependencyProperty SelectedElementProperty =
DependencyProperty.Register("SelectedElement", typeof(IElementEditor), typeof(ElementsEditor), new PropertyMetadata(null, (d, e) => { (d as ElementsEditor).OnSelectedElementChanged(); }));
///
/// Gets or sets the selected elements.
///
public ObservableCollection SelectedElements
{
get { return (ObservableCollection)GetValue(SelectedElementsProperty); }
set { SetValue(SelectedElementsProperty, value); }
}
public static readonly DependencyProperty SelectedElementsProperty =
DependencyProperty.Register("SelectedElements", typeof(ObservableCollection), typeof(ElementsEditor), new PropertyMetadata(null));
///
/// Gets or sets the height of the ruler.
///
public double RulerHeight
{
get { return (double)GetValue(RulerHeightProperty); }
set { SetValue(RulerHeightProperty, value); }
}
public static readonly DependencyProperty RulerHeightProperty =
DependencyProperty.Register("RulerHeight", typeof(double), typeof(ElementsEditor), new PropertyMetadata(22.0));
///
/// Gets or sets the undo redo states provider.
///
public IUndoRedoStatesProvider UndoRedoStatesProvider
{
get { return (IUndoRedoStatesProvider)GetValue(UndoRedoStatesProviderProperty); }
set { SetValue(UndoRedoStatesProviderProperty, value); }
}
public static readonly DependencyProperty UndoRedoStatesProviderProperty =
DependencyProperty.Register("UndoRedoStatesProvider", typeof(IUndoRedoStatesProvider), typeof(ElementsEditor), new PropertyMetadata(null));
///
/// Gets or sets a value indicating whether to bring the selected element to the front z index.
///
public bool BringToFrontOnSelect
{
get { return (bool)GetValue(BringToFrontOnSelectProperty); }
set { SetValue(BringToFrontOnSelectProperty, value); }
}
public static readonly DependencyProperty BringToFrontOnSelectProperty =
DependencyProperty.Register("BringToFrontOnSelect", typeof(bool), typeof(ElementsEditor), new PropertyMetadata(true));
///
/// Gets or sets the editor mode.
///
public ElementsEditorMode EditorMode
{
get { return (ElementsEditorMode)GetValue(EditorModeProperty); }
set { SetValue(EditorModeProperty, value); }
}
public static readonly DependencyProperty EditorModeProperty =
DependencyProperty.Register("EditorMode", typeof(ElementsEditorMode), typeof(ElementsEditor), new PropertyMetadata(ElementsEditorMode.Default));
///
/// Gets or sets a value indicating whether to enable the creation of new elements using the mouse and Shift key.
///
public bool EnableElementCreation
{
get { return (bool)GetValue(EnableElementCreationProperty); }
set { SetValue(EnableElementCreationProperty, value); }
}
public static readonly DependencyProperty EnableElementCreationProperty =
DependencyProperty.Register("EnableElementCreation", typeof(bool), typeof(ElementsEditor), new PropertyMetadata(true));
///
/// Gets or sets an optional attached elements editor for editors mirroring mode.
///
public ElementsEditor AttachedEditor
{
get { return (ElementsEditor)GetValue(AttachedEditorProperty); }
set { SetValue(AttachedEditorProperty, value); }
}
public static readonly DependencyProperty AttachedEditorProperty =
DependencyProperty.Register("AttachedEditor", typeof(ElementsEditor), typeof(ElementsEditor), new PropertyMetadata(null, (d, e) => (d as ElementsEditor).OnAttachedEditorChanged()));
///
/// Gets or sets the preview visual source opacity.
///
public double PreviewVisualSourceOpacity
{
get { return (double)GetValue(PreviewVisualSourceOpacityProperty); }
set { SetValue(PreviewVisualSourceOpacityProperty, value); }
}
public static readonly DependencyProperty PreviewVisualSourceOpacityProperty =
DependencyProperty.Register("PreviewVisualSourceOpacity", typeof(double), typeof(ElementsEditor), new PropertyMetadata(1.0));
///
/// Gets or sets a value indicating whether this instance is editable.
///
public bool IsEditable
{
get { return (bool)GetValue(IsEditableProperty); }
set { SetValue(IsEditableProperty, value); }
}
public static readonly DependencyProperty IsEditableProperty =
DependencyProperty.Register("IsEditable", typeof(bool), typeof(ElementsEditor), new PropertyMetadata(true));
#endregion
#region Attached Properties
#region IsSelected
///
/// Determines whether the element is currently selected.
///
public static readonly DependencyProperty IsSelectedProperty =
DependencyProperty.RegisterAttached("IsSelected",
typeof(bool), typeof(ElementsEditor),
new FrameworkPropertyMetadata(false));
///
/// Sets the IsSelected attached property.
///
/// The element.
/// if set to true selected.
public static void SetIsSelected(IElementEditor element, bool value)
{
(element as DependencyObject).SetValue(IsSelectedProperty, value);
}
///
/// Gets the is IsSelected attached property.
///
/// The element.
///
public static bool GetIsSelected(IElementEditor element)
{
return (bool)(element as DependencyObject).GetValue(IsSelectedProperty);
}
#endregion
#endregion
#region Theme Properties
///
/// Gets or sets the ruler background.
///
public Brush RulerBackground
{
get { return (Brush)GetValue(RulerBackgroundProperty); }
set { SetValue(RulerBackgroundProperty, value); }
}
public static readonly DependencyProperty RulerBackgroundProperty =
DependencyProperty.Register("RulerBackground", typeof(Brush), typeof(ElementsEditor), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender));
///
/// Gets or sets the editor background.
///
public Brush EditorBackground
{
get { return (Brush)GetValue(EditorBackgroundProperty); }
set { SetValue(EditorBackgroundProperty, value); }
}
public static readonly DependencyProperty EditorBackgroundProperty =
DependencyProperty.Register("EditorBackground", typeof(Brush), typeof(ElementsEditor), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender));
///
/// Gets or sets the selection fill brush.
///
public Brush SelectionFillBrush
{
get { return (Brush)GetValue(SelectionFillBrushProperty); }
set { SetValue(SelectionFillBrushProperty, value); }
}
public static readonly DependencyProperty SelectionFillBrushProperty =
DependencyProperty.Register("SelectionFillBrush", typeof(Brush), typeof(ElementsEditor), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender));
///
/// Gets or sets the selection stroke brush.
///
public Brush SelectionStrokeBrush
{
get { return (Brush)GetValue(SelectionStrokeBrushProperty); }
set { SetValue(SelectionStrokeBrushProperty, value); }
}
public static readonly DependencyProperty SelectionStrokeBrushProperty =
DependencyProperty.Register("SelectionStrokeBrush", typeof(Brush), typeof(ElementsEditor), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender));
#endregion
#region Virtual Methods
///
/// Invoked when the attached editor has changed.
///
protected virtual void OnAttachedEditorChanged()
{
if (AttachedEditor != null)
{
UndoRedoStatesProvider = new AttachedElementsEditorsUndoRedoStatesProvider(this, AttachedEditor);
AttachedEditor.UndoRedoStatesProvider = UndoRedoStatesProvider;
}
}
///
/// Raises the event.
///
/// The instance containing the event data.
protected virtual void OnElementsAdded(ElementsEventArgs e)
{
if (ElementsAdded != null) ElementsAdded(this, e);
}
///
/// Raises the event.
///
/// The instance containing the event data.
protected virtual void OnElementsRemoved(ElementsEventArgs e)
{
if (ElementsRemoved != null) ElementsRemoved(this, e);
}
///
/// Called when the reset scale factor button was clicked.
///
/// The sender.
/// The instance containing the event data.
protected virtual void OnResetScaleFactor(object sender, MouseButtonEventArgs e)
{
ScaleFactor = 1;
}
///
/// Called when the selected element has changed.
///
protected virtual void OnSelectedElementChanged()
{
if (SelectedElement != null)
{
if (BringToFrontOnSelect)
{
if (Elements.Count > 0)
{
Canvas.SetZIndex(SelectedElement as UIElement, Elements.Max(x => Canvas.GetZIndex(x as UIElement) + 1));
}
}
SetElementSelection(SelectedElement, true);
if (AttachedEditor != null && AttachedEditor.SelectedElement != SelectedElement.AttachedEditor)
{
AttachedEditor.SelectedElement = SelectedElement.AttachedEditor;
}
}
else
{
if (AttachedEditor != null)
{
AttachedEditor.SelectedElement = null;
}
}
if (!IsCtrlDown())
{
foreach (var element in Elements.Where(x => x != SelectedElement))
{
SetElementSelection(element, false);
}
}
OnSelectionChanged();
InvalidateRelayCommands();
}
///
/// Raises the event.
///
protected void OnSelectionChanged()
{
if (SelectionChanged != null) SelectionChanged(this, new ElementsEventArgs(GetSelectedElements()));
}
///
/// Called when the elements collection has changed.
///
protected virtual void OnElementsChanged()
{
if (Elements != null)
{
RegisterElementsEvents();
Elements.CollectionChanged -= Elements_CollectionChanged;
Elements.CollectionChanged += Elements_CollectionChanged;
SetCanvasElements();
}
InvalidateRelayCommands();
}
///
/// Called when the hosting canvas has captured a mouse down event.
///
/// The sender.
/// The instance containing the event data.
protected virtual void OnCanvasMouseDown(object sender, MouseButtonEventArgs e)
{
DeselectElements();
_selectionMouseDownPoint = e.GetPosition(selectionCanvas);
_selectionPerformed = false;
_isSelectionMouseDown = true;
selectionRec.Width = 0;
selectionRec.Height = 0;
selectionCanvas.Visibility = System.Windows.Visibility.Visible;
}
///
/// Handles the canvas mouse up event.
///
/// The sender.
/// The instance containing the event data.
protected virtual void OnCanvasMouseUp(object sender, MouseButtonEventArgs e)
{
if (_selectionPerformed)
{
_selectionPerformed = false;
selectionCanvas.Visibility = System.Windows.Visibility.Hidden;
e.Handled = true;
if (IsShiftDown() && EnableElementCreation)
{
OnElementCreation();
}
}
_isSelectionMouseDown = false;
}
///
/// Raises the event.
///
protected virtual void OnElementCreation()
{
PrepareUndoState();
var args = new ElementCreationEventArgs(Canvas.GetLeft(selectionRec), Canvas.GetTop(selectionRec), selectionRec.Width, selectionRec.Height);
if (ElementCreation != null) ElementCreation(this, args);
if (args.AppendUndoState)
{
CommitUndoState();
}
}
///
/// Handles the canvas mouse move event.
///
/// The sender.
/// The instance containing the event data.
protected virtual void OnCanvasMouseMove(object sender, MouseEventArgs e)
{
if (_isSelectionMouseDown)
{
Point currentMousePoint = e.GetPosition(selectionCanvas);
_selectionPerformed = currentMousePoint.X != _selectionMouseDownPoint.X || currentMousePoint.Y != _selectionMouseDownPoint.Y;
Canvas.SetLeft(selectionRec, _selectionMouseDownPoint.X);
Canvas.SetTop(selectionRec, _selectionMouseDownPoint.Y);
if (currentMousePoint.X - _selectionMouseDownPoint.X > 1)
{
selectionRec.Width = currentMousePoint.X - _selectionMouseDownPoint.X;
}
if (currentMousePoint.Y - _selectionMouseDownPoint.Y > 1)
{
selectionRec.Height = currentMousePoint.Y - _selectionMouseDownPoint.Y;
}
if (currentMousePoint.X < _selectionMouseDownPoint.X)
{
Canvas.SetLeft(selectionRec, currentMousePoint.X);
selectionRec.Width = _selectionMouseDownPoint.X - currentMousePoint.X;
}
if (currentMousePoint.Y < _selectionMouseDownPoint.Y)
{
Canvas.SetTop(selectionRec, currentMousePoint.Y);
selectionRec.Height = _selectionMouseDownPoint.Y - currentMousePoint.Y;
}
if (!IsShiftDown())
{
//Select intersecting objects
Rect selectRect = new Rect(Canvas.GetLeft(selectionRec), Canvas.GetTop(selectionRec), selectionRec.Width, selectionRec.Height);
var allElements = GetAllElements();
foreach (var element in allElements)
{
SetElementSelection(element, GetElementBounds(element).IntersectsWith(selectRect));
}
var selectedElements = GetSelectedElements();
if (selectedElements.Count == 1)
{
SelectedElement = selectedElements.FirstOrDefault();
}
}
}
}
///
/// Called when coercing the scale factor.
///
/// The value.
///
protected virtual object OnCoerceScaleFactor(object value)
{
if ((double)value < 0.1)
{
return 0.1;
}
else
{
return value;
}
}
///
/// Called when zooming with mouse wheel.
///
/// The sender.
/// The instance containing the event data.
protected virtual void OnMouseWheelZooming(object sender, MouseWheelEventArgs e)
{
if (e.Delta > 0) //Ticks up
{
ScaleFactor += 0.1;
}
else //Ticks Down
{
ScaleFactor -= 0.1;
}
Point pointAbsolute = Mouse.GetPosition(gridCanvas);
Point pointRelative = Mouse.GetPosition(scrollViewer);
scrollViewer.ScrollToHorizontalOffset((pointAbsolute.X * ScaleFactor) - pointRelative.X);
scrollViewer.ScrollToVerticalOffset((pointAbsolute.Y * ScaleFactor) - pointRelative.Y);
}
#endregion
#region Event Handlers
///
/// Handles the Loaded event of the ElementsEditor control.
///
/// The source of the event.
/// The instance containing the event data.
private void ElementsEditor_Loaded(object sender, RoutedEventArgs e)
{
SetCanvasElements();
}
///
/// Handles the CollectionChanged event of the Elements control.
///
/// The source of the event.
/// The instance containing the event data.
private void Elements_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
RegisterElementsEvents();
SetCanvasElements();
if (e.NewItems != null && e.NewItems.Count > 0)
{
OnElementsAdded(new ElementsEventArgs(e.NewItems.Cast().ToList()));
}
if (e.OldItems != null && e.OldItems.Count > 0)
{
OnElementsRemoved(new ElementsEventArgs(e.OldItems.Cast().ToList()));
}
}
///
/// Handles the SelectionChanged event of the Element control.
///
/// The source of the event.
/// The instance containing the event data.
///
private void Element_SelectionChanged(object sender, EventArgs e)
{
IElementEditor element = sender as IElementEditor;
SelectedElement = element;
}
///
/// Handles the Moving event of the Element control.
///
/// The source of the event.
/// The instance containing the event data.
///
private void Element_Moving(object sender, System.Windows.Controls.Primitives.DragDeltaEventArgs e)
{
if (IsCtrlDown())
{
GetSelectedElements().Where(x => x != sender).ToList().ForEach(x => x.PushMove(e));
}
}
///
/// Handles the AfterBoundsChange event of the Element control.
///
/// The source of the event.
/// The instance containing the event data.
private void Element_AfterBoundsChange(object sender, EventArgs e)
{
CommitUndoState();
}
///
/// Handles the BeforeBoundsChange event of the Element control.
///
/// The source of the event.
/// The instance containing the event data.
private void Element_BeforeBoundsChange(object sender, EventArgs e)
{
PrepareUndoState();
}
///
/// Handles the PreviewMouseDown event of the Element control.
///
/// The source of the event.
/// The instance containing the event data.
private void Element_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
if (e.ChangedButton == MouseButton.Left && !Keyboard.IsKeyDown(Key.LeftShift))
{
SelectedElement = sender as IElementEditor;
}
}
#endregion
#region Private Methods
private void SetCanvasElements()
{
if (!this.IsInDesignMode())
{
if (canvas != null && Elements != null)
{
foreach (UIElement element in Elements)
if (!canvas.Children.Contains(element))
canvas.Children.Add(element);
List removeList = new List();
foreach (UIElement element in canvas.Children)
if (!Elements.Contains(element as IElementEditor))
removeList.Add(element);
foreach (UIElement element in removeList)
canvas.Children.Remove(element);
}
}
}
///
/// Determines whether the control key is down.
///
private bool IsCtrlDown()
{
return Keyboard.IsKeyDown(Key.LeftCtrl);
}
///
/// Determines whether the shift control is down.
///
private bool IsShiftDown()
{
return Keyboard.IsKeyDown(Key.LeftShift);
}
///
/// Registers the elements events.
///
private void RegisterElementsEvents()
{
foreach (var element in Elements)
{
element.Moving -= Element_Moving;
element.Moving += Element_Moving;
element.BeforeBoundsChange -= Element_BeforeBoundsChange;
element.BeforeBoundsChange += Element_BeforeBoundsChange;
element.AfterBoundsChange -= Element_AfterBoundsChange;
element.AfterBoundsChange += Element_AfterBoundsChange;
if (element is FrameworkElement)
{
(element as FrameworkElement).PreviewMouseDown -= Element_PreviewMouseDown;
(element as FrameworkElement).PreviewMouseDown += Element_PreviewMouseDown;
(element as FrameworkElement).PreviewMouseDown -= ElementsEditor_PreviewMouseDown;
(element as FrameworkElement).PreviewMouseDown += ElementsEditor_PreviewMouseDown;
}
}
}
private void ElementsEditor_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
if (e.ClickCount == 2)
{
ElementDoubleClicked?.Invoke(this, sender as IElementEditor);
}
}
///
/// Prepares the state of the undo.
///
private void PrepareUndoState()
{
UndoRedoStatesProvider.PrepareUndoState();
}
///
/// Commits the state of the undo.
///
private void CommitUndoState()
{
UndoRedoStatesProvider.CommitUndoState();
}
///
/// Executes the undo/redo state.
///
/// The state.
private void ExecuteUndoRedoState(List state)
{
foreach (var setup in state)
{
setup.Animation.Completed += (x, y) =>
{
var aniValue = setup.DependencyObject.GetValue(setup.DependencyProperty);
(setup.DependencyObject as IAnimatable).BeginAnimation(setup.DependencyProperty, null);
setup.DependencyObject.SetCurrentValue(setup.DependencyProperty, aniValue);
};
(setup.DependencyObject as IAnimatable).BeginAnimation(setup.DependencyProperty, setup.Animation);
}
}
///
/// Creates an undo/redo state.
///
///
private List CreateUndoRedoState()
{
List all = new List();
foreach (var element in Elements)
{
var setups = element.GetAnimationSetups(TimeSpan.FromSeconds(0), AnimationSetupMode.Discrete);
all.AddRange(setups);
}
return all;
}
///
/// Gets the element bounds.
///
/// The element.
///
private Rect GetElementBounds(IElementEditor element)
{
var visual = element as FrameworkElement;
if (visual != null)
{
var position = visual.TranslatePoint(new Point(0, 0), selectionCanvas);
return new Rect(position.X, position.Y, visual.ActualWidth, visual.ActualHeight);
}
else
{
return new Rect();
}
}
///
/// Gets the visual child.
///
///
/// The parent.
///
private static T GetVisualChild(DependencyObject parent) where T : Visual
{
T child = default(T);
int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < numVisuals; i++)
{
Visual v = (Visual)VisualTreeHelper.GetChild(parent, i);
child = v as T;
if (child == null)
{
child = GetVisualChild(v);
}
if (child != null)
{
break;
}
}
return child;
}
#endregion
#region Public Methods
///
/// De-selects all elements.
///
public void DeselectElements()
{
Elements.ToList().ForEach(x => SetElementSelection(x, false));
SelectedElement = null;
OnSelectionChanged();
InvalidateRelayCommands();
}
///
/// Selects all elements.
///
public void SelectAll()
{
Elements.ToList().ForEach(x => SetElementSelection(x, true));
OnSelectionChanged();
InvalidateRelayCommands();
}
///
/// Increase or decrease the by the specified factor.
///
/// The factor (e.g 0.2 or -0.5).
public void Zoom(double factor)
{
ScaleFactor += factor;
}
///
/// Gets the selected elements.
///
public List GetSelectedElements()
{
return GetAllElements().Where(x => GetIsSelected(x)).ToList();
}
///
/// Gets all elements.
///
///
public List GetAllElements()
{
return Elements.ToList();
}
///
/// Removes the selected elements.
///
public void RemoveSelectedElements(bool raiseRemoveEvent = false)
{
ElementsEventArgs args = new ElementsEventArgs();
if (raiseRemoveEvent)
{
args.Elements = GetSelectedElements();
if (RemovingElements != null) RemovingElements(this, args);
}
if (!raiseRemoveEvent || !args.Cancel)
{
PrepareUndoState();
var selectedElements = GetSelectedElements();
selectedElements.ForEach(x => RemoveElement(x));
if (AttachedEditor != null)
{
foreach (var element in selectedElements)
{
if (element.AttachedEditor != null)
{
AttachedEditor.RemoveElement(element.AttachedEditor);
}
}
}
CommitUndoState();
OnSelectionChanged();
InvalidateRelayCommands();
}
}
///
/// Removes the element.
///
/// The element.
public void RemoveElement(IElementEditor element)
{
SetElementSelection(element, false);
Elements.Remove(element);
if (SelectedElement == element) SelectedElement = null;
OnSelectionChanged();
InvalidateRelayCommands();
}
///
/// Gets the element by the hosted element.
///
/// The hosted element.
public IElementEditor GetElementByHostedElement(object hostedElement)
{
return Elements.SingleOrDefault(x => x.HostedElement == hostedElement);
}
///
/// Undoes the current state of the elements collection.
///
public void Undo()
{
UndoRedoStatesProvider.Undo();
InvalidateRelayCommands();
}
///
/// Redoes the current state of the elements collection.
///
public void Redo()
{
UndoRedoStatesProvider.Redo();
InvalidateRelayCommands();
}
///
/// Performs copy operation on the selected elements.
///
public void Copy()
{
Copy(false);
}
private void Copy(bool fromAttached)
{
_copiedElements = GetSelectedElements();
InvalidateRelayCommands();
if (AttachedEditor != null && !fromAttached)
{
AttachedEditor.Copy(true);
}
}
///
/// Performs a Cut operation over the selected elements.
///
public void Cut()
{
Copy();
RemoveSelectedElements();
InvalidateRelayCommands();
}
///
/// Pastes the last copied elements.
///
public void Paste()
{
Paste(false, Mouse.GetPosition(gridCanvas), null);
}
private void Paste(bool fromAttached, Point point, List fromAttachedElements)
{
if (_copiedElements != null)
{
PrepareUndoState();
List elementsToAdd = new List();
var clonedElements = _copiedElements.Select(x => x.Clone()).ToList();
if (clonedElements.Count == 0) return;
var mostLeft = clonedElements.OrderBy(x => x.Left).First().Left;
var mostTop = clonedElements.OrderBy(x => x.Top).First().Top;
int count = 0;
foreach (var element in clonedElements)
{
element.Left += point.X - mostLeft;
element.Top += point.Y - mostTop;
elementsToAdd.Add(element);
if (fromAttachedElements != null && fromAttachedElements.Count > 0)
{
fromAttachedElements[count].AttachedEditor = element;
element.AttachedEditor = fromAttachedElements[count++];
}
}
if (AttachedEditor != null && !fromAttached)
{
AttachedEditor.Paste(true, point, clonedElements);
}
if (!fromAttached)
{
if (AfterPaste != null) AfterPaste(this, new ElementsEventArgs(clonedElements));
}
foreach (var element in elementsToAdd)
{
Elements.Add(element);
}
CommitUndoState();
}
InvalidateRelayCommands();
}
///
/// Brings the new element Z-index in front of the old element.
///
/// The old element.
/// The new element.
public void BringInFront(IElementEditor oldElement, IElementEditor newElement)
{
Canvas.SetZIndex(newElement as UIElement, Canvas.GetZIndex(oldElement as UIElement) + 1);
}
///
/// Brings the specified element to the Z-index front.
///
/// The element.
public void BringToFront(IElementEditor element)
{
element.ZIndex = Elements.Max(x => Canvas.GetZIndex(x as UIElement) + 1);
}
///
/// Brings all the selected items to the front.
///
public void BringToFront()
{
foreach (var element in GetSelectedElements())
{
BringToFront(element);
}
}
///
/// Sends all the selected items to the back.
///
public void SendToBack()
{
foreach (var element in GetSelectedElements())
{
SendToBack(element);
}
}
///
/// Sets the element Z-index.
///
/// The element.
/// The index.
public void SetZIndex(IElementEditor element, int index)
{
Canvas.SetZIndex(element as UIElement, index);
}
///
/// Gets the element Z-index.
///
/// The element.
///
public int GetZIndex(IElementEditor element)
{
return Canvas.GetZIndex(element as UIElement);
}
///
/// Sends the specified element to the Z-index back.
///
/// The element.
public void SendToBack(IElementEditor element)
{
element.ZIndex = Elements.Min(x => Canvas.GetZIndex(x as UIElement) - 1);
}
#endregion
#region Keyboard
///
/// Invoked when an unhandled attached event reaches an element in its route that is derived from this class. Implement this method to add class handling for this event.
///
/// The that contains the event data.
protected override void OnPreviewKeyDown(KeyEventArgs e)
{
base.OnPreviewKeyDown(e);
if (!IsEditable) return;
if (e.Key == Key.Right)
{
GetSelectedElements().ForEach(x => x.PushMove(new DragDeltaEventArgs(1, 0)));
}
else if (e.Key == Key.Left)
{
GetSelectedElements().ForEach(x => x.PushMove(new DragDeltaEventArgs(-1, 0)));
}
else if (e.Key == Key.Up)
{
GetSelectedElements().ForEach(x => x.PushMove(new DragDeltaEventArgs(0, -1)));
}
else if (e.Key == Key.Down)
{
GetSelectedElements().ForEach(x => x.PushMove(new DragDeltaEventArgs(0, 1)));
}
else if (e.Key == Key.Z && IsCtrlDown() && EnableKeyboardUndoRedoOperations)
{
Undo();
}
else if (e.Key == Key.Y && IsCtrlDown() && EnableKeyboardUndoRedoOperations)
{
Redo();
}
else if (e.Key == Key.Delete)
{
RemoveSelectedElements(true);
}
else if (e.Key == Key.C && IsCtrlDown() && EnableKeyboardEditingOperations)
{
Copy();
}
else if (e.Key == Key.V && IsCtrlDown() && EnableKeyboardEditingOperations)
{
Paste();
}
else if (e.Key == Key.X && IsCtrlDown() && EnableKeyboardEditingOperations)
{
Cut();
}
else if (e.Key == Key.A && IsCtrlDown())
{
SelectAll();
}
else if (e.Key == Key.F && IsCtrlDown())
{
BringToFront();
}
else if (e.Key == Key.B && IsCtrlDown())
{
SendToBack();
}
}
#endregion
#region Override Methods
///
/// Invoked when an unhandled attached event is raised on this element. Implement this method to add class handling for this event.
///
/// The that contains the event data.
protected override void OnMouseEnter(MouseEventArgs e)
{
base.OnMouseEnter(e);
//scrollViewer.Focus();
}
#endregion
#region Commands
///
/// Gets or sets the copy command.
///
public RelayCommand CopyCommand
{
get { return (RelayCommand)GetValue(CopyCommandProperty); }
set { SetValue(CopyCommandProperty, value); }
}
public static readonly DependencyProperty CopyCommandProperty =
DependencyProperty.Register("CopyCommand", typeof(RelayCommand), typeof(ElementsEditor), new PropertyMetadata(null));
///
/// Gets or sets the paste command.
///
public RelayCommand PasteCommand
{
get { return (RelayCommand)GetValue(PasteCommandProperty); }
set { SetValue(PasteCommandProperty, value); }
}
public static readonly DependencyProperty PasteCommandProperty =
DependencyProperty.Register("PasteCommand", typeof(RelayCommand), typeof(ElementsEditor), new PropertyMetadata(null));
///
/// Gets or sets the undo command.
///
public RelayCommand UndoCommand
{
get { return (RelayCommand)GetValue(UndoCommandProperty); }
set { SetValue(UndoCommandProperty, value); }
}
public static readonly DependencyProperty UndoCommandProperty =
DependencyProperty.Register("UndoCommand", typeof(RelayCommand), typeof(ElementsEditor), new PropertyMetadata(null));
///
/// Gets or sets the redo command.
///
public RelayCommand RedoCommand
{
get { return (RelayCommand)GetValue(RedoCommandProperty); }
set { SetValue(RedoCommandProperty, value); }
}
public static readonly DependencyProperty RedoCommandProperty =
DependencyProperty.Register("RedoCommand", typeof(RelayCommand), typeof(ElementsEditor), new PropertyMetadata(null));
///
/// Gets or sets the delete command.
///
public RelayCommand DeleteCommand
{
get { return (RelayCommand)GetValue(DeleteCommandProperty); }
set { SetValue(DeleteCommandProperty, value); }
}
public static readonly DependencyProperty DeleteCommandProperty =
DependencyProperty.Register("DeleteCommand", typeof(RelayCommand), typeof(ElementsEditor), new PropertyMetadata(null));
///
/// Gets or sets the cut command.
///
public RelayCommand CutCommand
{
get { return (RelayCommand)GetValue(CutCommandProperty); }
set { SetValue(CutCommandProperty, value); }
}
public static readonly DependencyProperty CutCommandProperty =
DependencyProperty.Register("CutCommand", typeof(RelayCommand), typeof(ElementsEditor), new PropertyMetadata(null));
///
/// Gets or sets the select all command.
///
public RelayCommand SelectAllCommand
{
get { return (RelayCommand)GetValue(SelectAllCommandProperty); }
set { SetValue(SelectAllCommandProperty, value); }
}
public static readonly DependencyProperty SelectAllCommandProperty =
DependencyProperty.Register("SelectAllCommand", typeof(RelayCommand), typeof(ElementsEditor), new PropertyMetadata(null));
///
/// Gets or sets the zoom command.
///
public RelayCommand ZoomCommand
{
get { return (RelayCommand)GetValue(ZoomCommandProperty); }
set { SetValue(ZoomCommandProperty, value); }
}
public static readonly DependencyProperty ZoomCommandProperty =
DependencyProperty.Register("ZoomCommand", typeof(RelayCommand), typeof(ElementsEditor), new PropertyMetadata(null));
///
/// Gets or sets the reset zoom command.
///
public RelayCommand ResetZoomCommand
{
get { return (RelayCommand)GetValue(ResetZoomCommandProperty); }
set { SetValue(ResetZoomCommandProperty, value); }
}
public static readonly DependencyProperty ResetZoomCommandProperty =
DependencyProperty.Register("ResetZoomCommand", typeof(RelayCommand), typeof(ElementsEditor), new PropertyMetadata(null));
///
/// Invokes bring to front on the selected item.
///
public RelayCommand BringToFrontCommand
{
get { return (RelayCommand)GetValue(BringToFrontCommandProperty); }
set { SetValue(BringToFrontCommandProperty, value); }
}
public static readonly DependencyProperty BringToFrontCommandProperty =
DependencyProperty.Register("BringToFrontCommand", typeof(RelayCommand), typeof(ElementsEditor), new PropertyMetadata(null));
///
/// Invokes send to back on the selected item.
///
public RelayCommand SendToBackCommand
{
get { return (RelayCommand)GetValue(SendToBackCommandProperty); }
set { SetValue(SendToBackCommandProperty, value); }
}
public static readonly DependencyProperty SendToBackCommandProperty =
DependencyProperty.Register("SendToBackCommand", typeof(RelayCommand), typeof(ElementsEditor), new PropertyMetadata(null));
#endregion
#region IConfigurable Members
///
/// Gets a configuration object representing the configurable properties. This configuration can be serialized to a stream or file, and later be loaded and applied to the configurable object.
///
///
public virtual IConfiguration GetConfiguration()
{
return GetConfiguration();
}
///
/// Gets the configuration.
///
/// Name of the configuration.
///
public virtual ElementsEditorConfiguration GetConfiguration(String configurationName)
{
ElementsEditorConfiguration config = new ElementsEditorConfiguration();
config.Name = configurationName;
config.Date = DateTime.Now;
config.ElementsConfigurations = new ObservableCollection(Elements.Select(x => x.GetConfiguration() as ElementEditorConfiguration).ToList());
return config;
}
///
/// Applies the specified configuration with an optional animation if supported by the configurable type.
///
/// The configuration.
/// The animation.
///
public virtual void SetConfiguration(IConfiguration configuration, ConfigurationAnimation animation)
{
if (!(configuration is ElementsEditorConfiguration))
{
throw new InvalidConfigurationException();
}
UndoRedoStatesProvider.Reset();
var config = configuration as ElementsEditorConfiguration;
foreach (var elementConfig in config.ElementsConfigurations)
{
var existingElement = Elements.SingleOrDefault(x => x.ID == elementConfig.ID);
if (existingElement != null)
{
existingElement.SetConfiguration(elementConfig, animation);
}
else
{
var newElement = elementConfig.CreateConfigurable();
Elements.Add(newElement);
}
}
var elementsToRemove = Elements.Where(x => !config.ElementsConfigurations.ToList().Exists(y => y.ID == x.ID)).ToList();
elementsToRemove.ForEach(x => RemoveElement(x));
InvalidateRelayCommands();
}
#endregion
private void SetElementSelection(IElementEditor element, bool selected)
{
SetIsSelected(element, selected);
if (element.AttachedEditor != null && GetIsSelected(element.AttachedEditor) != selected)
{
ElementsEditor.SetIsSelected(element.AttachedEditor, selected);
}
}
}
}