From 901b1ce927228aea0d102b74a4a489348e75349d Mon Sep 17 00:00:00 2001 From: Kumar Devvrat Date: Wed, 7 Jul 2010 14:43:58 +0000 Subject: [PATCH] - Add Context menu with general set of operations. - Add short-cut extensibility in the designer. - Add Focus navigation(actually the primary selection navigation) up-the-element-tree and down-the-element tree though Tab and Shift+Tab respectively. - Add edit operations for the designer - Cut,Copy and Paste by copying Xaml of the element to clipboard and loading it, works with more than one instances of SharpDevelop too. git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/branches/wpfdesigner@6066 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61 --- .../Src/Commands/CutCopyPaste.cs | 81 +++++++ .../WpfDesign.AddIn/Src/Commands/Pads.cs | 45 ++++ .../WpfDesign.AddIn/Src/Commands/Remove.cs | 40 ++++ .../WpfDesign.AddIn/Src/Commands/UndoRedo.cs | 65 ++++++ .../WpfDesign.AddIn/Src/Commands/ViewXaml.cs | 23 ++ .../WpfDesign.AddIn/Src/WpfViewContent.cs | 5 + .../WpfDesign.AddIn/WpfDesign.AddIn.csproj | 6 + .../WpfDesign/WpfDesign.AddIn/WpfDesign.addin | 59 +++-- .../Project/DesignCommand.cs | 46 ++++ .../Project/DesignSurface.xaml.cs | 42 +++- .../Project/FocusNavigator.cs | 219 ++++++++++++++++++ .../Project/Services/DesignerKeyBindings.cs | 52 +++++ .../Project/WpfDesign.Designer.csproj | 4 + .../Project/Xaml/XamlDesignContext.cs | 11 +- .../Project/Xaml/XamlEditOperations.cs | 167 +++++++++++++ .../Project/WpfDesign.XamlDom.csproj | 4 +- .../WpfDesign.XamlDom/Project/XamlParser.cs | 31 +++ .../Project/XamlStaticTools.cs | 27 +++ .../WpfDesign/WpfDesign/Project/Services.cs | 34 +++ 19 files changed, 936 insertions(+), 25 deletions(-) create mode 100644 src/AddIns/DisplayBindings/WpfDesign/WpfDesign.AddIn/Src/Commands/CutCopyPaste.cs create mode 100644 src/AddIns/DisplayBindings/WpfDesign/WpfDesign.AddIn/Src/Commands/Pads.cs create mode 100644 src/AddIns/DisplayBindings/WpfDesign/WpfDesign.AddIn/Src/Commands/Remove.cs create mode 100644 src/AddIns/DisplayBindings/WpfDesign/WpfDesign.AddIn/Src/Commands/UndoRedo.cs create mode 100644 src/AddIns/DisplayBindings/WpfDesign/WpfDesign.AddIn/Src/Commands/ViewXaml.cs create mode 100644 src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/DesignCommand.cs create mode 100644 src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/FocusNavigator.cs create mode 100644 src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Services/DesignerKeyBindings.cs create mode 100644 src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Xaml/XamlEditOperations.cs create mode 100644 src/AddIns/DisplayBindings/WpfDesign/WpfDesign.XamlDom/Project/XamlStaticTools.cs diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.AddIn/Src/Commands/CutCopyPaste.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.AddIn/Src/Commands/CutCopyPaste.cs new file mode 100644 index 0000000000..f5285c4ace --- /dev/null +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.AddIn/Src/Commands/CutCopyPaste.cs @@ -0,0 +1,81 @@ +// +// +// +// +// $Revision: $ +// +using System; +using ICSharpCode.Core; +using ICSharpCode.WpfDesign.Designer; + +namespace ICSharpCode.WpfDesign.AddIn.Commands +{ + /// + /// Invokes Cut command on the Design surface. + /// + class Cut : AbstractMenuCommand + { + public override void Run() + { + var surface = Owner as DesignSurface; + if(surface!=null) + surface.Cut(); + } + } + + /// + /// Invokes Copy command on the Design surface. + /// + class Copy : AbstractMenuCommand + { + public override void Run() + { + var surface = Owner as DesignSurface; + if (surface != null) + surface.Copy(); + + } + } + + /// + /// Invokes Paste operation on the Design surface. + /// + class Paste : AbstractMenuCommand + { + public override void Run() + { + var surface = Owner as DesignSurface; + if (surface != null) + surface.Paste(); + } + } + + /// + /// Provides implementation of for and . + /// + class IsCutCopyEnabled : IConditionEvaluator + { + public bool IsValid(object owner, Condition condition) + { + var surface = owner as DesignSurface; + if(surface!=null) { + return surface.CanCopyOrCut(); + } + return false; + } + } + + /// + /// Provides implementation of for . + /// + class IsPasteEnabled : IConditionEvaluator + { + public bool IsValid(object owner, Condition condition) + { + var surface = owner as DesignSurface; + if (surface != null) + return surface.CanPaste(); + return false; + } + } +} diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.AddIn/Src/Commands/Pads.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.AddIn/Src/Commands/Pads.cs new file mode 100644 index 0000000000..1d7121f6f1 --- /dev/null +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.AddIn/Src/Commands/Pads.cs @@ -0,0 +1,45 @@ +// +// +// +// +// $Revision: $ +// +using System; +using ICSharpCode.Core; +using ICSharpCode.SharpDevelop.Gui; + +namespace ICSharpCode.WpfDesign.AddIn.Commands +{ + /// + /// Opens up the Tools Pad. + /// + class Tools : AbstractMenuCommand + { + public override void Run() + { + WorkbenchSingleton.Workbench.GetPad(typeof(ToolsPad)).BringPadToFront(); + } + } + + /// + /// Opens up the Propeties Pad. + /// + class Properties : AbstractMenuCommand + { + public override void Run() + { + WorkbenchSingleton.Workbench.GetPad(typeof(PropertyPad)).BringPadToFront(); + } + } + + /// + /// Opens up the Outline Pad. + /// + class Outline : AbstractMenuCommand + { + public override void Run() + { + WorkbenchSingleton.Workbench.GetPad(typeof(OutlinePad)).BringPadToFront(); + } + } +} diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.AddIn/Src/Commands/Remove.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.AddIn/Src/Commands/Remove.cs new file mode 100644 index 0000000000..ce7cfb3746 --- /dev/null +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.AddIn/Src/Commands/Remove.cs @@ -0,0 +1,40 @@ +// +// +// +// +// $Revision: $ +// +using System; +using ICSharpCode.Core; + +using ICSharpCode.WpfDesign.Designer; + +namespace ICSharpCode.WpfDesign.AddIn.Commands +{ + /// + /// Removes selected element from the designer. + /// + class Remove : AbstractMenuCommand + { + public override void Run() + { + var surface = Owner as DesignSurface; + if (surface != null) + surface.Delete(); + } + } + + /// + /// Provides implementation of for . + /// + class IsRemoveEnabled : IConditionEvaluator + { + public bool IsValid(object owner, Condition condition) + { + var surface = owner as DesignSurface; + if (surface != null) + return surface.CanDelete(); + return false; + } + } +} diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.AddIn/Src/Commands/UndoRedo.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.AddIn/Src/Commands/UndoRedo.cs new file mode 100644 index 0000000000..e9df7bca8d --- /dev/null +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.AddIn/Src/Commands/UndoRedo.cs @@ -0,0 +1,65 @@ +// +// +// +// +// $Revision: $ +// +using System; +using ICSharpCode.Core; +using ICSharpCode.WpfDesign.Designer; +namespace ICSharpCode.WpfDesign.AddIn.Commands +{ + /// + /// Invokes the Undo command if available on the Design Surface. + /// + class Undo : AbstractMenuCommand + { + public override void Run() + { + var surface = Owner as DesignSurface; + if (surface != null) + surface.Undo(); + } + } + + /// + /// Invokes the Redo command if available on the Design surface. + /// + class Redo : AbstractMenuCommand + { + public override void Run() + { + var surface = Owner as DesignSurface; + if (surface != null) + surface.Redo(); + } + } + + /// + /// Provides implementation of for . + /// + class IsUndoEnabled : IConditionEvaluator + { + public bool IsValid(object owner, Condition condition) + { + var surface = owner as DesignSurface; + if (surface != null) + return surface.CanUndo(); + return false; + } + } + + /// + /// Provides implementation of for . + /// + class IsRedoEnabled : IConditionEvaluator + { + public bool IsValid(object owner, Condition condition) + { + var surface = owner as DesignSurface; + if (surface != null) + return surface.CanRedo(); + return false; + } + } +} diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.AddIn/Src/Commands/ViewXaml.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.AddIn/Src/Commands/ViewXaml.cs new file mode 100644 index 0000000000..7d85b84093 --- /dev/null +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.AddIn/Src/Commands/ViewXaml.cs @@ -0,0 +1,23 @@ +// +// +// +// +// $Revision: $ +// +using System; +using ICSharpCode.Core; +using ICSharpCode.SharpDevelop.Gui; + +namespace ICSharpCode.WpfDesign.AddIn.Commands +{ + /// + /// Switches to the XAML source code tab. + /// + public class ViewXaml : AbstractMenuCommand + { + public override void Run() + { + WorkbenchSingleton.Workbench.ActiveWorkbenchWindow.SwitchView(0); + } + } +} diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.AddIn/Src/WpfViewContent.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.AddIn/Src/WpfViewContent.cs index 36ec94ca6f..9c5a89e41b 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.AddIn/Src/WpfViewContent.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.AddIn/Src/WpfViewContent.cs @@ -6,6 +6,7 @@ // using System; +using System.Windows; using System.Collections.Generic; using System.Diagnostics; using System.IO; @@ -26,6 +27,8 @@ using ICSharpCode.WpfDesign.Designer.Services; using ICSharpCode.WpfDesign.Designer.Xaml; using ICSharpCode.WpfDesign.PropertyGrid; +using ICSharpCode.Core.Presentation; + namespace ICSharpCode.WpfDesign.AddIn { /// @@ -87,6 +90,8 @@ namespace ICSharpCode.WpfDesign.AddIn designer.LoadDesigner(r, settings); + designer.ContextMenuOpening += (sender, e) => MenuService.ShowContextMenu(e.OriginalSource as UIElement, designer, "/Addins/WpfDesign/Designer/ContextMenu"); + UpdateTasks(); if (outline != null && designer.DesignContext != null && designer.DesignContext.RootItem != null) { diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.AddIn/WpfDesign.AddIn.csproj b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.AddIn/WpfDesign.AddIn.csproj index b9f8349eb9..89765e5295 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.AddIn/WpfDesign.AddIn.csproj +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.AddIn/WpfDesign.AddIn.csproj @@ -60,6 +60,11 @@ + + + + + @@ -146,5 +151,6 @@ + \ No newline at end of file diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.AddIn/WpfDesign.addin b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.AddIn/WpfDesign.addin index b0c5cd9091..50cd3f037e 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.AddIn/WpfDesign.addin +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.AddIn/WpfDesign.addin @@ -1,30 +1,28 @@ - - + - - + + - - + + + + + + + + + + - - + - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/DesignCommand.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/DesignCommand.cs new file mode 100644 index 0000000000..ec87502a46 --- /dev/null +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/DesignCommand.cs @@ -0,0 +1,46 @@ +// +// +// +// +// $Revision: $ +// +using System; +using System.Windows.Input; +using System.Diagnostics; + +namespace ICSharpCode.WpfDesign.Designer +{ + /// + /// Custom implementation of ICommand based on the RelayCommand model by Josh Smith for the designer. + /// + public class DesignCommand : ICommand + { + private readonly Action _action; + private readonly Func _canExecute; + + public DesignCommand(Action action,Func canExecute) + { + Debug.Assert(action != null); + this._action = action; + this._canExecute = canExecute; + } + + public void Execute(object parameter) + { + _action(parameter); + } + + public bool CanExecute(object parameter) + { + if (_canExecute != null) + return _canExecute(parameter); + return true; + } + + public event EventHandler CanExecuteChanged + { + add { CommandManager.RequerySuggested += value; } + remove { CommandManager.RequerySuggested -= value; } + } + } +} diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/DesignSurface.xaml.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/DesignSurface.xaml.cs index 3d164e670d..50162f9da3 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/DesignSurface.xaml.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/DesignSurface.xaml.cs @@ -33,6 +33,7 @@ namespace ICSharpCode.WpfDesign.Designer /// public partial class DesignSurface { + private FocusNavigator _focusNav; static DesignSurface() { //TODO: this is for converters (see PropertyGrid) @@ -45,8 +46,8 @@ namespace ICSharpCode.WpfDesign.Designer this.AddCommandHandler(ApplicationCommands.Undo, Undo, CanUndo); this.AddCommandHandler(ApplicationCommands.Redo, Redo, CanRedo); - this.AddCommandHandler(ApplicationCommands.Copy, Copy, HasSelection); - this.AddCommandHandler(ApplicationCommands.Cut, Cut, HasSelection); + this.AddCommandHandler(ApplicationCommands.Copy, Copy, CanCopyOrCut); + this.AddCommandHandler(ApplicationCommands.Cut, Cut, CanCopyOrCut); this.AddCommandHandler(ApplicationCommands.Delete, Delete, CanDelete); this.AddCommandHandler(ApplicationCommands.Paste, Paste, CanPaste); this.AddCommandHandler(ApplicationCommands.SelectAll, SelectAll, CanSelectAll); @@ -114,6 +115,10 @@ namespace ICSharpCode.WpfDesign.Designer context.Services.Selection.SelectionChanged += delegate { CommandManager.InvalidateRequerySuggested(); }; + + context.Services.AddService(typeof(IKeyBindingService), new DesignerKeyBindings(this)); + _focusNav=new FocusNavigator(this); + _focusNav.Start(); } /// @@ -121,12 +126,14 @@ namespace ICSharpCode.WpfDesign.Designer /// public void UnloadDesigner() { + if(_focusNav!=null) + _focusNav.End(); if (_designContext != null) { foreach (object o in _designContext.Services.AllServices) { IDisposable d = o as IDisposable; if (d != null) d.Dispose(); } - } + } _designContext = null; _designPanel.Context = null; _sceneContainer.Child = null; @@ -165,17 +172,32 @@ namespace ICSharpCode.WpfDesign.Designer _designContext.Services.Selection.SetSelectedComponents(GetLiveElements(action.AffectedElements)); } - public bool HasSelection() + public bool CanCopyOrCut() { - return false; + ISelectionService selectionService = GetService(); + if (selectionService.SelectedItems.Count == 0) + return false; + if (selectionService.SelectedItems.Count == 1 && selectionService.PrimarySelection == DesignContext.RootItem) + return false; + return true; } public void Copy() { + XamlDesignContext xamlContext = _designContext as XamlDesignContext; + ISelectionService selection = _designContext.Services.Selection; + if(xamlContext != null && selection != null){ + xamlContext.XamlEditAction.Copy(selection.SelectedItems); + } } public void Cut() { + XamlDesignContext xamlContext = _designContext as XamlDesignContext; + ISelectionService selection = _designContext.Services.Selection; + if(xamlContext != null && selection != null){ + xamlContext.XamlEditAction.Cut(selection.SelectedItems); + } } public bool CanDelete() @@ -195,11 +217,21 @@ namespace ICSharpCode.WpfDesign.Designer public bool CanPaste() { + ISelectionService selection = _designContext.Services.Selection; + if(selection.SelectedItems.Count!=0){ + string xaml = Clipboard.GetText(TextDataFormat.Xaml); + if(xaml != "" && xaml != " ") + return true; + } return false; } public void Paste() { + XamlDesignContext xamlContext = _designContext as XamlDesignContext; + if(xamlContext != null){ + xamlContext.XamlEditAction.Paste(); + } } public bool CanSelectAll() diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/FocusNavigator.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/FocusNavigator.cs new file mode 100644 index 0000000000..52f97769a2 --- /dev/null +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/FocusNavigator.cs @@ -0,0 +1,219 @@ +// +// +// +// +// $Revision: $ +// +using System; +using System.Linq; +using System.Windows.Input; + +namespace ICSharpCode.WpfDesign.Designer +{ + /// + /// Manages the Focus/Primary Selection using TAB for down-the-tree navigation and Shift+TAB for up-the-tree navigation. + /// + class FocusNavigator + { + /* The Focus navigator do not involves the concept of Logical Focus or KeyBoard Focus + * since nothing is getting focoused on the designer except for the DesignPanel. It just changes + * the primary selection between the hierarchy of elements present on the designer. */ + + private readonly DesignSurface _surface; + private KeyBinding _tabBinding; + private KeyBinding _shiftTabBinding; + + public FocusNavigator(DesignSurface surface) + { + this._surface=surface; + } + + /// + /// Starts the navigator on the Design surface and add bindings. + /// + public void Start() + { + DesignCommand tabFocus = new DesignCommand(parameter => this.MoveFocusForward(_surface), parameter => CanMoveFocusForward(_surface)); + DesignCommand shiftTabFocus = new DesignCommand(parameter => this.MoveFocusBack(_surface), parameter => this.CanMoveFocusBack(_surface)); + _tabBinding = new KeyBinding(tabFocus, new KeyGesture(Key.Tab)); + _shiftTabBinding = new KeyBinding(shiftTabFocus, new KeyGesture(Key.Tab, ModifierKeys.Shift)); + IKeyBindingService kbs = _surface.DesignContext.Services.GetService(typeof(IKeyBindingService)) as IKeyBindingService; + if (kbs != null) + { + kbs.RegisterBinding(_tabBinding); + kbs.RegisterBinding(_shiftTabBinding); + } + } + + /// + /// De-register the bindings from the Design Surface + /// + public void End() + { + IKeyBindingService kbs = _surface.DesignContext.Services.GetService(typeof(IKeyBindingService)) as IKeyBindingService; + if (kbs != null) + { + kbs.DeregisterBinding(_tabBinding); + kbs.DeregisterBinding(_shiftTabBinding); + } + } + + /// + /// Moves the Foucus down the tree. + /// + void MoveFocusForward(object surface) + { + var designSurface = surface as DesignSurface; + if (designSurface != null) { + var context = designSurface.DesignContext; + ISelectionService selection=context.Services.Selection; + DesignItem item = selection.PrimarySelection; + selection.SetSelectedComponents(selection.SelectedItems, SelectionTypes.Remove); + if (item != GetLastElement()) + { + if (item.ContentProperty != null) + { + if (item.ContentProperty.IsCollection) + { + if (item.ContentProperty.CollectionElements.Count != 0) + { + if (ModelTools.CanSelectComponent(item.ContentProperty.CollectionElements.First())) + selection.SetSelectedComponents(new DesignItem[] { item.ContentProperty.CollectionElements.First() }, SelectionTypes.Primary); + else + SelectNextInPeers(item); + } + else + SelectNextInPeers(item); + } + else if (item.ContentProperty.Value != null) { + if (ModelTools.CanSelectComponent(item.ContentProperty.Value)) + selection.SetSelectedComponents(new DesignItem[] { item.ContentProperty.Value }, SelectionTypes.Primary); + else + SelectNextInPeers(item); + } + else { + SelectNextInPeers(item); + } + } + else { + SelectNextInPeers(item); + } + } + else { //if the element was last element move focus to the root element to keep a focus cycle. + selection.SetSelectedComponents(new DesignItem[] {context.RootItem}, SelectionTypes.Primary); + } + } + } + + /// + /// Checks if focus navigation should be for down-the-tree be done. + /// + /// Design Surface + bool CanMoveFocusForward(object surface) + { + var designSurface = surface as DesignSurface; + if (designSurface != null) + if (Keyboard.FocusedElement == designSurface._designPanel) + return true; + return false; + } + + /// + /// Moves focus up-the-tree. + /// + void MoveFocusBack(object surface) + { + var designSurface = surface as DesignSurface; + if (designSurface != null) { + var context = designSurface.DesignContext; + ISelectionService selection = context.Services.Selection; + DesignItem item = selection.PrimarySelection; + if (item != context.RootItem) + { + if (item.Parent != null && item.Parent.ContentProperty.IsCollection) + { + int index = item.Parent.ContentProperty.CollectionElements.IndexOf(item); + if (index != 0) + { + if (ModelTools.CanSelectComponent(item.Parent.ContentProperty.CollectionElements.ElementAt(index - 1))) + selection.SetSelectedComponents(new DesignItem[] { item.Parent.ContentProperty.CollectionElements.ElementAt(index - 1) }, SelectionTypes.Primary); + } + else + { + if (ModelTools.CanSelectComponent(item.Parent)) + selection.SetSelectedComponents(new DesignItem[] { item.Parent }, SelectionTypes.Primary); + } + + } + else + { + if (ModelTools.CanSelectComponent(item.Parent)) + selection.SetSelectedComponents(new DesignItem[] { item.Parent }, SelectionTypes.Primary); + } + } + else {// if the element was root item move focus again to the last element. + selection.SetSelectedComponents(new DesignItem[] { GetLastElement() }, SelectionTypes.Primary); + } + } + } + + /// + /// Checks if focus navigation for the up-the-tree should be done. + /// + /// Design Surface + bool CanMoveFocusBack(object surface) + { + var designSurface = surface as DesignSurface; + if (designSurface != null) + if (Keyboard.FocusedElement == designSurface._designPanel) + return true; + return false; + } + + /// + /// Gets the last element in the element hierarchy. + /// + DesignItem GetLastElement() + { + DesignItem item = _surface.DesignContext.RootItem; + while (item != null && item.ContentProperty != null) + { + if (item.ContentProperty.IsCollection) + { + if (item.ContentProperty.CollectionElements.Count != 0) { + if (ModelTools.CanSelectComponent(item.ContentProperty.CollectionElements.Last())) + item = item.ContentProperty.CollectionElements.Last(); + else + break; + } + else + break; + } + else { + if (item.ContentProperty.Value != null) + item = item.ContentProperty.Value; + else + break; + } + } + return item; + } + + /// + /// Select the next element in the element collection if parent's had it's content property as collection. + /// + void SelectNextInPeers(DesignItem item) + { + ISelectionService selection = _surface.DesignContext.Services.Selection; + if (item.Parent != null && item.Parent.ContentProperty != null) + { + if (item.Parent.ContentProperty.IsCollection) + { + int index = item.Parent.ContentProperty.CollectionElements.IndexOf(item); + if (index != item.Parent.ContentProperty.CollectionElements.Count) + selection.SetSelectedComponents(new DesignItem[] { item.Parent.ContentProperty.CollectionElements.ElementAt(index + 1) }, SelectionTypes.Primary); + } + } + } + } +} diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Services/DesignerKeyBindings.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Services/DesignerKeyBindings.cs new file mode 100644 index 0000000000..f07a7a7066 --- /dev/null +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Services/DesignerKeyBindings.cs @@ -0,0 +1,52 @@ +// +// +// +// +// $Revision: $ +// +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Linq; +using System.Windows.Input; + +namespace ICSharpCode.WpfDesign.Designer.Services +{ + class DesignerKeyBindings : IKeyBindingService + { + private readonly DesignSurface _surface; + private Collection _bindings; + + public DesignerKeyBindings(DesignSurface surface) + { + Debug.Assert(surface != null); + this._surface = surface; + _bindings = new Collection(); + } + + public void RegisterBinding(KeyBinding binding) + { + if(binding!=null) { + _surface.InputBindings.Add(binding); + _bindings.Add(binding); + } + } + + public void DeregisterBinding(KeyBinding binding) + { + if(_bindings.Contains(binding)) { + _surface.InputBindings.Remove(binding); + _bindings.Remove(binding); + } + } + + public KeyBinding GetBinding(KeyGesture gesture) + { + return _bindings.FirstOrDefault(binding => binding.Key == gesture.Key && binding.Modifiers == gesture.Modifiers); + } + + public object Owner{ + get { return _surface; } + } + + } +} diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/WpfDesign.Designer.csproj b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/WpfDesign.Designer.csproj index 98dce7957b..5ac8276a68 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/WpfDesign.Designer.csproj +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/WpfDesign.Designer.csproj @@ -104,6 +104,7 @@ + DesignSurface.xaml @@ -127,6 +128,7 @@ Code + @@ -195,6 +197,7 @@ + @@ -210,6 +213,7 @@ + diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Xaml/XamlDesignContext.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Xaml/XamlDesignContext.cs index c1854deeec..9e81c2a4ce 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Xaml/XamlDesignContext.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Xaml/XamlDesignContext.cs @@ -25,9 +25,15 @@ namespace ICSharpCode.WpfDesign.Designer.Xaml public sealed class XamlDesignContext : DesignContext { readonly XamlDocument _doc; - readonly XamlDesignItem _rootItem; + readonly XamlDesignItem _rootItem; internal readonly XamlComponentService _componentService; + readonly XamlEditOperations _xamlEditOperations; + + internal XamlEditOperations XamlEditAction { + get { return _xamlEditOperations; } + } + internal XamlDocument Document { get { return _doc; } } @@ -85,7 +91,10 @@ namespace ICSharpCode.WpfDesign.Designer.Xaml parserSettings.ServiceProvider = this.Services; _doc = XamlParser.Parse(xamlReader, parserSettings); _rootItem = _componentService.RegisterXamlComponentRecursive(_doc.RootElement); + + _xamlEditOperations=new XamlEditOperations(this,parserSettings); } + /// /// Saves the XAML DOM into the XML writer. diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Xaml/XamlEditOperations.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Xaml/XamlEditOperations.cs new file mode 100644 index 0000000000..34befdebfc --- /dev/null +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Xaml/XamlEditOperations.cs @@ -0,0 +1,167 @@ +// +// +// +// +// $Revision: $ +// +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Windows; + +using ICSharpCode.WpfDesign.XamlDom; + +namespace ICSharpCode.WpfDesign.Designer.Xaml +{ + /// + /// Deals with operations on controls which also require access to internal XML properties of the XAML Document. + /// + public class XamlEditOperations + { + readonly XamlDesignContext _context; + readonly XamlParserSettings _settings; + + /// + /// Delimet character to seperate different piece of Xaml's + /// + readonly char _delimeter = Convert.ToChar(0x7F); + + public XamlEditOperations(XamlDesignContext context, XamlParserSettings settings) + { + this._context = context; + this._settings = settings; + } + + /// + /// Copy from the designer to clipboard. + /// + public void Cut(ICollection designItems) + { + Clipboard.Clear(); + string cutXaml = ""; + var changeGroup = _context.OpenGroup("Cut " + designItems.Count + " elements", designItems); + foreach (var item in designItems) + { + if (item != null && item != _context.RootItem) + { + XamlDesignItem xamlItem = item as XamlDesignItem; + if (xamlItem != null) { + cutXaml += XamlStaticTools.GetXaml(xamlItem.XamlObject); + cutXaml += _delimeter; + } + } + } + ModelTools.DeleteComponents(designItems); + Clipboard.SetText(cutXaml, TextDataFormat.Xaml); + changeGroup.Commit(); + } + + /// + /// Copy from the designer to clipboard. + /// + public void Copy(ICollection designItems) + { + Clipboard.Clear(); + string copiedXaml = ""; + var changeGroup = _context.OpenGroup("Copy " + designItems.Count + " elements", designItems); + foreach (var item in designItems) + { + if (item != null) + { + XamlDesignItem xamlItem = item as XamlDesignItem; + if (xamlItem != null) { + copiedXaml += XamlStaticTools.GetXaml(xamlItem.XamlObject); + copiedXaml += _delimeter; + } + } + } + Clipboard.SetText(copiedXaml, TextDataFormat.Xaml); + changeGroup.Commit(); + } + + /// + /// Paste items from clipboard into the designer. + /// + public void Paste() + { + bool pasted = false; + string combinedXaml = Clipboard.GetText(TextDataFormat.Xaml); + IEnumerable xamls = combinedXaml.Split(_delimeter); + xamls = xamls.Where(xaml => xaml != ""); + + DesignItem parent = _context.Services.Selection.PrimarySelection; + DesignItem child = _context.Services.Selection.PrimarySelection; + + XamlDesignItem rootItem = _context.RootItem as XamlDesignItem; + var pastedItems = new Collection(); + foreach(var xaml in xamls) { + var obj = XamlParser.ParseSnippet(rootItem.XamlObject, xaml, _settings); + if(obj!=null) { + DesignItem item = _context._componentService.RegisterXamlComponentRecursive(obj); + if (item != null) + pastedItems.Add(item); + } + } + + if (pastedItems.Count != 0) { + var changeGroup = _context.OpenGroup("Paste " + pastedItems.Count + " elements", pastedItems); + while (parent != null && pasted == false) { + if (parent.ContentProperty != null) { + if (parent.ContentProperty.IsCollection) { + if (CollectionSupport.CanCollectionAdd(parent.ContentProperty.ReturnType, pastedItems.Select(item => item.Component))) { + AddInParent(parent, pastedItems); + pasted = true; + } + } else if (pastedItems.Count == 1 && parent.ContentProperty.Value == null && parent.ContentProperty.ValueOnInstance == null) { + AddInParent(parent, pastedItems); + pasted = true; + } else { + parent = parent.Parent; + } + } else { + parent = parent.Parent; + } + } + + while (pasted == false) { + if (child.ContentProperty != null) { + if (child.ContentProperty.IsCollection) { + foreach (var col in child.ContentProperty.CollectionElements) { + if (col.ContentProperty != null && col.ContentProperty.IsCollection) { + if (CollectionSupport.CanCollectionAdd(col.ContentProperty.ReturnType, pastedItems.Select(item => item.Component))) { + pasted = true; + } + } + } + break; + } else if (child.ContentProperty.Value != null) { + child = child.ContentProperty.Value; + } else if (pastedItems.Count == 1) { + child.ContentProperty.SetValue(pastedItems.First().Component); + pasted = true; + break; + } else + break; + } else + break; + } + changeGroup.Commit(); + } + } + + /// + /// Adds Items under a parent given that the content property is collection and can add types of + /// + /// The Parent element + /// The list of elements to be added + void AddInParent(DesignItem parent,IList pastedItems) + { + IEnumerable rects = pastedItems.Select(i => new Rect(new Point(0, 0), new Point((double)i.Properties["Width"].ValueOnInstance, (double)i.Properties["Height"].ValueOnInstance))); + var operation = PlacementOperation.TryStartInsertNewComponents(parent, pastedItems, rects.ToList(), PlacementType.AddItem); + ISelectionService selection = _context.Services.Selection; + selection.SetSelectedComponents(pastedItems); + operation.Commit(); + } + } +} diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.XamlDom/Project/WpfDesign.XamlDom.csproj b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.XamlDom/Project/WpfDesign.XamlDom.csproj index d0b267d53d..c603e5881b 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.XamlDom/Project/WpfDesign.XamlDom.csproj +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.XamlDom/Project/WpfDesign.XamlDom.csproj @@ -1,4 +1,5 @@ - + + {88DA149F-21B2-48AB-82C4-28FB6BDFD783} Debug @@ -81,6 +82,7 @@ + diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.XamlDom/Project/XamlParser.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.XamlDom/Project/XamlParser.cs index 727029c5fc..d63fe41463 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.XamlDom/Project/XamlParser.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.XamlDom/Project/XamlParser.cs @@ -577,5 +577,36 @@ namespace ICSharpCode.WpfDesign.XamlDom return converter.ConvertFromInvariantString( scope.OwnerDocument.GetTypeDescriptorContext(scope), valueText); } + + /// + /// Method use to parse a piece of Xaml. + /// + /// The Root XamlObject of the current document. + /// The Xaml being parsed. + /// Parser settings used by . + /// Returns the XamlObject of the parsed . + public static XamlObject ParseSnippet(XamlObject root, string xaml, XamlParserSettings settings) + { + XmlTextReader reader = new XmlTextReader(new StringReader(xaml)); + var element = root.OwnerDocument.XmlDocument.ReadNode(reader); + + if (element != null) { + XmlAttribute xmlnsAttribute=null; + foreach (XmlAttribute attrib in element.Attributes) { + if (attrib.Name == "xmlns") + xmlnsAttribute = attrib; + } + if(xmlnsAttribute!=null) + element.Attributes.Remove(xmlnsAttribute); + + XamlParser parser = new XamlParser(); + parser.settings = settings; + parser.document = root.OwnerDocument; + var xamlObject = parser.ParseObject(element as XmlElement); + if (xamlObject != null) + return xamlObject; + } + return null; + } } } diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.XamlDom/Project/XamlStaticTools.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.XamlDom/Project/XamlStaticTools.cs new file mode 100644 index 0000000000..a42b4cb15e --- /dev/null +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.XamlDom/Project/XamlStaticTools.cs @@ -0,0 +1,27 @@ +// +// +// +// +// $Revision: $ +// +using System; + +namespace ICSharpCode.WpfDesign.XamlDom +{ + /// + /// Static methods to help with designer operations which require access to internal Xaml elements. + /// + public static class XamlStaticTools + { + /// + /// Gets the Xaml string of the + /// + /// The object whose Xaml is requested. + public static string GetXaml(XamlObject xamlObject) + { + if (xamlObject != null) + return xamlObject.XmlElement.OuterXml; + return null; + } + } +} diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Services.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Services.cs index 616c75fdbc..053cdc4560 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Services.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Services.cs @@ -6,6 +6,7 @@ // using System; +using System.Windows.Input; using System.Collections.Generic; using System.Windows; @@ -217,4 +218,37 @@ namespace ICSharpCode.WpfDesign ITopLevelWindow GetTopLevelWindow(UIElement element); } #endregion + + #region IKeyBindingService + + /// + /// Service that handles all the key bindings in the designer. + /// + public interface IKeyBindingService + { + /// + /// Gets the object to which the bindings are being applied + /// + object Owner { get; } + + /// + /// Register with . + /// + /// The binding to be applied. + void RegisterBinding(KeyBinding binding); + + /// + /// De-register with . + /// + /// The binding to be applied. + void DeregisterBinding(KeyBinding binding); + + /// + /// Gets binding for the corresponding gesture otherwise returns null. + /// + /// The keyboard gesture requested. + KeyBinding GetBinding(KeyGesture gesture); + } + + #endregion }