From f4e60aae5478518ed08793044a7d2d205478b4cf Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Tue, 27 Feb 2007 20:44:10 +0000 Subject: [PATCH] Add Undo/Redo support to WpfDesigner. git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@2410 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61 --- .../StandaloneDesigner/Images/Redo.png | Bin 0 -> 724 bytes .../StandaloneDesigner/Images/Undo.png | Bin 0 -> 788 bytes .../StandaloneDesigner.csproj | 3 + .../WpfDesign/StandaloneDesigner/Window1.xaml | 48 +++---- .../StandaloneDesigner/Window1.xaml.cs | 7 ++ .../Project/DesignSurface.cs | 51 ++++++++ .../Extensions/DefaultChildResizeSupport.cs | 15 ++- .../Extensions/ResizeThumbExtension.cs | 11 +- .../Project/Services/UndoService.cs | 53 ++++++-- .../Project/Xaml/XamlModelProperty.cs | 118 +++++++++++++----- .../Project/PropertyEditor/TextBoxEditor.cs | 4 +- 11 files changed, 237 insertions(+), 73 deletions(-) create mode 100644 src/AddIns/DisplayBindings/WpfDesign/StandaloneDesigner/Images/Redo.png create mode 100644 src/AddIns/DisplayBindings/WpfDesign/StandaloneDesigner/Images/Undo.png diff --git a/src/AddIns/DisplayBindings/WpfDesign/StandaloneDesigner/Images/Redo.png b/src/AddIns/DisplayBindings/WpfDesign/StandaloneDesigner/Images/Redo.png new file mode 100644 index 0000000000000000000000000000000000000000..9ce8542694b21793df61a5931775b0b339fe0bf4 GIT binary patch literal 724 zcmV;_0xSKAP)z@;j(q!3lK=n!AY({UO#lFTB>(_`g8%^e{{R4h=>PzA zFaQARU;qF*m;eA5Z<1fdMgRZ;N=ZaPRCwBA{Qv(y10?_;fLM?@7jC^Z=vaKY{uL9i z!SfHl1myV`E<~6KFHG=NTY#={!RA{DA3pyS00X{5;53i29!b44j+{3|w3c00G1Tan8+z zKZeEvXJ7ncuzh}c!~9OGL;+s54+qXZb10m1rvJi!Ap-_EIflfA59Wi^D@gG?fO!QV zfS5qGZ#?(b|2`8h!`qwp8R`>s8$p^uk`BgF2hLA-F-(y8wt(Tq3kC*ZVXz?!x7><{ z82}JKU@tK6@G$)T{*!^>(?NaB(vnIQ!fIJ!U{&iT6=k7@)wg0cHru{~!PmK#ZVJ1u1ZxcyB+0pdf?7 zoBI#$FZEFbNrJ^2&+P}f5DwVc85r2u;9P(JVuJZq;oHjx3_pG_JowL}&@k(41J0E5 z6X?~?pWzrFfZ*w1|Cy%_;J8gY^Pjc9E2w@eEY`0ASuZZ z*nW0B$p3_rGC%;KnD+kDPXU93&F2}CPl8<{5WMLj_GL}ey`0K#f2$R!SGoAWP)z@;j(q!3lK=n!AY({UO#lFTB>(_`g8%^e{{R4h=>PzA zFaQARU;qF*m;eA5Z<1fdMgRZ;ib+I4RCwBA{Qv(y10itW>@x=ttsu$!KtYQ40kRxG z05MigJJWyf^)G{PJIRRw4hkD!5+6SQ5}2_3a>2wCU-Cb2@Cz_7F)=W_eaqk=_UAx< zwo##hn(zgXI)DITw5dAtU&Y>-;oZ^W2R1fZI)Vf*+&mMfZzcN5kfS2t9Kmakl_`$$%^%GEzg$-=;g3Y%Q9NRDNf1qxxz`zI; zxO{~nfbrpm0;#tX1fJae0Ct^}fPW!gTw#=h~>*~U?6_^&T!${ zD}w_UUO6!6XfrUpe8mvJ_in@dR>wq;GhyKZ^0$K;&w&G<85}?Zq z3}3%8d=L;5U=RR`y?V`%AoqR2{A#B}1|k4J05LKA1{v^$fq|Eof#Lmo27$ZRKFEu5 zK5UqEwgH46Jo~JG95V;5eE>(P0Na1C%K!q1h2ba2fN#K%28JdhBf|$}ErG_f4E10g zfB!NZymH)r|0FkX?0}+`L0lXx9$+8{j>iVqy3KwEfj9?Ct|9R8Zi00E^nj4Ug*? zWMvr`o%l{ogjD-9Na(ZCx;q_q6z%WZN6IcLCkpKb2NGK_TG&^Q* z+kb&g%7B5HnL**h;|J&Gcp1WC2Oxl02-yzS{9hQXS>Wxn59`}(1CgT^Ab^+%834*a zV8(+74E^!y@H_wm00G1ROJ%t5`%gaw95T1{66A378A1-E9F4-4AD5-8946-D0EC06C2902F} WpfDesign + + + \ No newline at end of file diff --git a/src/AddIns/DisplayBindings/WpfDesign/StandaloneDesigner/Window1.xaml b/src/AddIns/DisplayBindings/WpfDesign/StandaloneDesigner/Window1.xaml index d0aa4321eb..b0d9485d57 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/StandaloneDesigner/Window1.xaml +++ b/src/AddIns/DisplayBindings/WpfDesign/StandaloneDesigner/Window1.xaml @@ -4,17 +4,22 @@ xmlns:designer="clr-namespace:ICSharpCode.WpfDesign.Designer;assembly=ICSharpCode.WpfDesign.Designer" Title="StandaloneDesigner" Height="500" Width="700" > - - - - - - - - + + + + + + + + + + + + ]]> - - - - - - - - - + + + + + + + + + + diff --git a/src/AddIns/DisplayBindings/WpfDesign/StandaloneDesigner/Window1.xaml.cs b/src/AddIns/DisplayBindings/WpfDesign/StandaloneDesigner/Window1.xaml.cs index a92c9954fd..73c686b713 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/StandaloneDesigner/Window1.xaml.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/StandaloneDesigner/Window1.xaml.cs @@ -4,6 +4,7 @@ using System.IO; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; +using System.Windows.Input; using System.Windows.Markup; using System.Xml; using ICSharpCode.WpfDesign; @@ -21,6 +22,11 @@ namespace StandaloneDesigner { try { InitializeComponent(); + foreach (object o in toolBar.Items) { + if (o is Button) { + (o as Button).CommandTarget = designSurface; + } + } } catch (Exception ex) { MessageBox.Show(ex.ToString()); Close(); @@ -32,6 +38,7 @@ namespace StandaloneDesigner TextBox CodeTextBox; DesignSurface designSurface; PropertyEditor propertyEditor; + ToolBar toolBar; #endif void tabControlSelectionChanged(object sender, RoutedEventArgs e) diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/DesignSurface.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/DesignSurface.cs index 0df8ca341f..f961073478 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/DesignSurface.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/DesignSurface.cs @@ -6,11 +6,14 @@ // using System; +using System.Diagnostics; using System.Windows; +using System.Windows.Input; using System.Windows.Controls; using System.Xml; using ICSharpCode.WpfDesign.Designer.Controls; +using ICSharpCode.WpfDesign.Designer.Services; namespace ICSharpCode.WpfDesign.Designer { @@ -34,7 +37,53 @@ namespace ICSharpCode.WpfDesign.Designer _scrollViewer.VerticalScrollBarVisibility = ScrollBarVisibility.Visible; _scrollViewer.HorizontalScrollBarVisibility = ScrollBarVisibility.Visible; this.VisualChild = _scrollViewer; + + this.CommandBindings.Add(new CommandBinding(ApplicationCommands.Undo, OnUndoExecuted, OnUndoCanExecute)); + this.CommandBindings.Add(new CommandBinding(ApplicationCommands.Redo, OnRedoExecuted, OnRedoCanExecute)); + } + + #region Undo/Redo + UndoService _undoService; + + private UndoService UndoService { + get { return _undoService; } + set { + if (_undoService != null) { + _undoService.UndoStackChanged -= OnUndoStackChanged; + } + _undoService = value; + if (_undoService != null) { + _undoService.UndoStackChanged += OnUndoStackChanged; + } + CommandManager.InvalidateRequerySuggested(); + } + } + + void OnUndoExecuted(object sender, ExecutedRoutedEventArgs e) + { + _undoService.Undo(); + } + + void OnUndoCanExecute(object sender, CanExecuteRoutedEventArgs e) + { + e.CanExecute = _undoService != null && _undoService.CanUndo; + } + + void OnRedoExecuted(object sender, ExecutedRoutedEventArgs e) + { + _undoService.Redo(); + } + + void OnRedoCanExecute(object sender, CanExecuteRoutedEventArgs e) + { + e.CanExecute = _undoService != null && _undoService.CanRedo; + } + + void OnUndoStackChanged(object sender, EventArgs e) + { + CommandManager.InvalidateRequerySuggested(); } + #endregion /// /// Gets the active design context. @@ -70,6 +119,7 @@ namespace ICSharpCode.WpfDesign.Designer designPanelBorder.Padding = new Thickness(10); _designPanel.Child = designPanelBorder; designPanelBorder.Child = context.RootItem.View; + UndoService = context.Services.GetService(); } /// @@ -82,6 +132,7 @@ namespace ICSharpCode.WpfDesign.Designer _designPanel.Child = null; _designPanel.Adorners.Clear(); _designPanel.MarkerCanvas.Children.Clear(); + UndoService = null; } } } diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Extensions/DefaultChildResizeSupport.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Extensions/DefaultChildResizeSupport.cs index e8a9bfdb72..5ccab5530d 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Extensions/DefaultChildResizeSupport.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Extensions/DefaultChildResizeSupport.cs @@ -66,10 +66,10 @@ namespace ICSharpCode.WpfDesign.Designer.Extensions public void Resize(DesignItem childItem, double horizontalChange, double verticalChange, HorizontalAlignment horizontal, VerticalAlignment vertical) { RelativePlacement p = (RelativePlacement)GetPlacement(childItem, horizontalChange, verticalChange, horizontal, vertical); - Resize(childItem, p, horizontalChange, verticalChange, horizontal, vertical); + Resize(childItem, p, horizontal, vertical); } - static void Resize(DesignItem childItem, RelativePlacement p, double horizontalChange, double verticalChange, HorizontalAlignment horizontal, VerticalAlignment vertical) + static void Resize(DesignItem childItem, RelativePlacement p, HorizontalAlignment horizontal, VerticalAlignment vertical) { DesignItemProperty margin = childItem.Properties[FrameworkElement.MarginProperty]; if (margin.IsSet) { @@ -81,6 +81,9 @@ namespace ICSharpCode.WpfDesign.Designer.Extensions margin.SetValue(t); } + double horizontalChange = p.WidthOffset; + double verticalChange = p.HeightOffset; + FrameworkElement child = (FrameworkElement)childItem.Component; double width = child.Width; double height = child.Height; @@ -130,8 +133,8 @@ namespace ICSharpCode.WpfDesign.Designer.Extensions rp.XOffset -= horizontalChange; rp.WidthOffset += horizontalChange; } else { - rp.XOffset -= horizontalChange / 2; - rp.WidthOffset += horizontalChange; + rp.XOffset -= horizontalChange; + rp.WidthOffset += horizontalChange * 2; } if (vertical == VerticalAlignment.Bottom) { @@ -140,8 +143,8 @@ namespace ICSharpCode.WpfDesign.Designer.Extensions rp.YOffset -= verticalChange; rp.HeightOffset += verticalChange; } else { - rp.YOffset -= verticalChange / 2; - rp.HeightOffset += verticalChange; + rp.YOffset -= verticalChange; + rp.HeightOffset += verticalChange * 2; } return rp; diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Extensions/ResizeThumbExtension.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Extensions/ResizeThumbExtension.cs index 1e85071f9f..b257301f63 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Extensions/ResizeThumbExtension.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Extensions/ResizeThumbExtension.cs @@ -85,10 +85,13 @@ namespace ICSharpCode.WpfDesign.Designer.Extensions adornerPanel.ClearValue(AdornerPanel.CursorProperty); if (resizeBehavior != null) { - resizeBehavior.Resize(this.ExtendedItem, - FixChange(e.HorizontalChange, horizontalAlignment), - FixChange(e.VerticalChange, verticalAlignment), - horizontalAlignment, verticalAlignment); + using (ChangeGroup group = this.ExtendedItem.OpenGroup("Resize")) { + resizeBehavior.Resize(this.ExtendedItem, + FixChange(e.HorizontalChange, horizontalAlignment), + FixChange(e.VerticalChange, verticalAlignment), + horizontalAlignment, verticalAlignment); + group.Commit(); + } } }; } diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Services/UndoService.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Services/UndoService.cs index ea0474b0ef..65132ab885 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Services/UndoService.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Services/UndoService.cs @@ -17,7 +17,7 @@ namespace ICSharpCode.WpfDesign.Designer.Services void Do(); void Undo(); - string Title { get; set; } + string Title { get; } } #endregion @@ -100,9 +100,10 @@ namespace ICSharpCode.WpfDesign.Designer.Services { AssertState(TransactionState.Undone); try { - for (int i = 0; i < items.Count; i--) { + for (int i = 0; i < items.Count; i++) { items[i].Do(); } + _state = TransactionState.Completed; } catch { _state = TransactionState.Failed; try { @@ -144,18 +145,10 @@ namespace ICSharpCode.WpfDesign.Designer.Services Stack _undoStack = new Stack(); Stack _redoStack = new Stack(); - UndoTransaction CurrentTransaction { - get { - if (_transactionStack.Count == 0) - return null; - else - return _transactionStack.Peek(); - } - } - internal UndoTransaction StartTransaction() { UndoTransaction t = new UndoTransaction(); + _transactionStack.Push(t); t.Committed += TransactionFinished; t.RolledBack += TransactionFinished; t.Committed += delegate(object sender, EventArgs e) { @@ -177,6 +170,7 @@ namespace ICSharpCode.WpfDesign.Designer.Services item.Do(); _undoStack.Push(item); _redoStack.Clear(); + OnUndoStackChanged(EventArgs.Empty); } else { _transactionStack.Peek().Execute(item); } @@ -189,6 +183,18 @@ namespace ICSharpCode.WpfDesign.Designer.Services get { return _undoStack.Count > 0; } } + /// + /// Is raised when the undo stack has changed. + /// + public event EventHandler UndoStackChanged; + + void OnUndoStackChanged(EventArgs e) + { + if (UndoStackChanged != null) { + UndoStackChanged(this, e); + } + } + /// /// Undoes the last action. /// @@ -202,6 +208,7 @@ namespace ICSharpCode.WpfDesign.Designer.Services try { item.Undo(); _redoStack.Push(item); + OnUndoStackChanged(EventArgs.Empty); } catch { // state might be invalid now, clear stacks to prevent getting more inconsistencies Clear(); @@ -209,6 +216,28 @@ namespace ICSharpCode.WpfDesign.Designer.Services } } + /// + /// Gets the list of names of the available actions on the undo stack. + /// + public IEnumerable UndoActions { + get { + foreach (ITransactionItem item in _undoStack) { + yield return item.Title; + } + } + } + + /// + /// Gets the list of names of the available actions on the undo stack. + /// + public IEnumerable RedoActions { + get { + foreach (ITransactionItem item in _redoStack) { + yield return item.Title; + } + } + } + /// /// Gets if there are redo actions available. /// @@ -227,6 +256,7 @@ namespace ICSharpCode.WpfDesign.Designer.Services try { item.Do(); _undoStack.Push(item); + OnUndoStackChanged(EventArgs.Empty); } catch { // state might be invalid now, clear stacks to prevent getting more inconsistencies Clear(); @@ -241,6 +271,7 @@ namespace ICSharpCode.WpfDesign.Designer.Services { _undoStack.Clear(); _redoStack.Clear(); + OnUndoStackChanged(EventArgs.Empty); } } #endregion diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Xaml/XamlModelProperty.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Xaml/XamlModelProperty.cs index 000bab5e3d..e4506eccd0 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Xaml/XamlModelProperty.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Xaml/XamlModelProperty.cs @@ -11,6 +11,7 @@ using System; using System.Diagnostics; using ICSharpCode.WpfDesign.XamlDom; +using ICSharpCode.WpfDesign.Designer.Services; namespace ICSharpCode.WpfDesign.Designer.Xaml { @@ -88,42 +89,28 @@ namespace ICSharpCode.WpfDesign.Designer.Xaml } } - #if EventHandlerDebugging - private event EventHandler _ValueChanged; - + // There may be multipley XamlModelProperty instances for the same property, + // so this class may not have any mutable fields / events - instead, + // we forward all event handlers to the XamlProperty. public override event EventHandler ValueChanged { add { + #if EventHandlerDebugging if (ValueChangedEventHandlers == 0) { Debug.WriteLine("ValueChangedEventHandlers is now > 0"); } ValueChangedEventHandlers++; - _ValueChanged += value; + #endif + _property.ValueChanged += value; } remove { + #if EventHandlerDebugging ValueChangedEventHandlers--; if (ValueChangedEventHandlers == 0) { Debug.WriteLine("ValueChangedEventHandlers reached 0"); } - _ValueChanged -= value; - } - } - #else - public override event EventHandler ValueChanged; - #endif - - void OnValueChanged(EventArgs e) - { - #if EventHandlerDebugging - if (_ValueChanged != null) { - _ValueChanged(this, EventArgs.Empty); - } - #else - if (ValueChanged != null) { - ValueChanged(this, EventArgs.Empty); + #endif + _property.ValueChanged -= value; } - #endif - - _designItem.NotifyPropertyChanged(this.Name); } public override object ValueOnInstance { @@ -162,10 +149,9 @@ namespace ICSharpCode.WpfDesign.Designer.Xaml public override void SetValue(object value) { - _property.ValueOnInstance = value; - + XamlPropertyValue newValue; if (value == null) { - _property.PropertyValue = _property.ParentObject.OwnerDocument.CreateNullValue(); + newValue = _property.ParentObject.OwnerDocument.CreateNullValue(); } else { XamlComponentService componentService = _designItem.ComponentService; @@ -173,22 +159,94 @@ namespace ICSharpCode.WpfDesign.Designer.Xaml if (designItem != null) { if (designItem.Parent != null) throw new DesignerException("Cannot set value to design item that already has a parent"); - _property.PropertyValue = designItem.XamlObject; + newValue = designItem.XamlObject; } else { XamlPropertyValue val = _property.ParentObject.OwnerDocument.CreatePropertyValue(value, _property); designItem = componentService.RegisterXamlComponentRecursive(val as XamlObject); - _property.PropertyValue = val; + newValue = val; } } - OnValueChanged(EventArgs.Empty); + UndoService undoService = _designItem.Services.GetService(); + if (undoService != null) + undoService.Execute(new PropertyChangeAction(this, true, newValue, value)); + else + SetValueInternal(value, newValue); + } + + void SetValueInternal(object value, XamlPropertyValue newValue) + { + _property.ValueOnInstance = value; + _property.PropertyValue = newValue; + + _designItem.NotifyPropertyChanged(this.Name); } public override void Reset() + { + UndoService undoService = _designItem.Services.GetService(); + if (undoService != null) + undoService.Execute(new PropertyChangeAction(this, false, null, null)); + else + ResetInternal(); + } + + void ResetInternal() { if (_property.IsSet) { _property.Reset(); - OnValueChanged(EventArgs.Empty); + _designItem.NotifyPropertyChanged(this.Name); + } + } + + sealed class PropertyChangeAction : ITransactionItem + { + XamlModelProperty property; + + bool newIsSet; + XamlPropertyValue newValue; + object newObject; + + XamlPropertyValue oldValue; + object oldObject; + bool oldIsSet; + + public PropertyChangeAction(XamlModelProperty property, bool newIsSet, XamlPropertyValue newValue, object newObject) + { + this.property = property; + + this.newIsSet = newIsSet; + this.newValue = newValue; + this.newObject = newObject; + + oldIsSet = property._property.IsSet; + oldValue = property._property.PropertyValue; + oldObject = property._property.ValueOnInstance; + } + + public string Title { + get { + if (newIsSet) + return "Set " + property.Name; + else + return "Reset " + property.Name; + } + } + + public void Do() + { + if (newIsSet) + property.SetValueInternal(newObject, newValue); + else + property.ResetInternal(); + } + + public void Undo() + { + if (oldIsSet) + property.SetValueInternal(oldObject, oldValue); + else + property.ResetInternal(); } } } diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/PropertyEditor/TextBoxEditor.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/PropertyEditor/TextBoxEditor.cs index 29faa2aedf..9392c5883c 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/PropertyEditor/TextBoxEditor.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/PropertyEditor/TextBoxEditor.cs @@ -35,7 +35,9 @@ namespace ICSharpCode.WpfDesign.PropertyEditor { this.property = property; UpdateFromSource(); - PropertyEditorBindingHelper.AddValueChangedEventHandler(this, property, delegate { UpdateFromSource(); }); + PropertyEditorBindingHelper.AddValueChangedEventHandler( + this, property, delegate { UpdateFromSource(); } + ); } void UpdateFromSource()