diff --git a/src/AddIns/DisplayBindings/WpfDesign/StandaloneDesigner/Window1.xaml b/src/AddIns/DisplayBindings/WpfDesign/StandaloneDesigner/Window1.xaml index 1fe110c423..61249c9042 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/StandaloneDesigner/Window1.xaml +++ b/src/AddIns/DisplayBindings/WpfDesign/StandaloneDesigner/Window1.xaml @@ -5,22 +5,20 @@ > - - - + - ]]> - + ]]> +// +// +// +// $Revision$ +// + +using System; +using System.Windows.Controls; +using System.Windows; +using System.Windows.Media; +using System.Windows.Shapes; +using System.Windows.Controls.Primitives; +using ICSharpCode.WpfDesign.Adorners; +using ICSharpCode.WpfDesign.Extensions; +using ICSharpCode.WpfDesign.Designer.Controls; + +namespace ICSharpCode.WpfDesign.Designer.Controls +{ + /// + /// A thumb where the look can depend on the IsPrimarySelection property. + /// Used by UIElementSelectionRectangle. + /// + public class ContainerDragHandle : Control + { + static ContainerDragHandle() + { + //This OverrideMetadata call tells the system that this element wants to provide a style that is different than its base class. + //This style is defined in themes\generic.xaml + DefaultStyleKeyProperty.OverrideMetadata(typeof(ContainerDragHandle), new FrameworkPropertyMetadata(typeof(ContainerDragHandle))); + } + } +} diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/ResizeThumb.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/ResizeThumb.cs new file mode 100644 index 0000000000..751ebf7332 --- /dev/null +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/ResizeThumb.cs @@ -0,0 +1,45 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Collections.Generic; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Controls.Primitives; +using ICSharpCode.WpfDesign.Adorners; +using ICSharpCode.WpfDesign.Extensions; + +namespace ICSharpCode.WpfDesign.Designer.Controls +{ + /// + /// A thumb where the look can depend on the IsPrimarySelection property. + /// Used by UIElementSelectionRectangle. + /// + public class ResizeThumb : Thumb + { + /// + /// Dependency property for . + /// + public static readonly DependencyProperty IsPrimarySelectionProperty + = DependencyProperty.Register("IsPrimarySelection", typeof(bool), typeof(ResizeThumb)); + + static ResizeThumb() + { + //This OverrideMetadata call tells the system that this element wants to provide a style that is different than its base class. + //This style is defined in themes\generic.xaml + DefaultStyleKeyProperty.OverrideMetadata(typeof(ResizeThumb), new FrameworkPropertyMetadata(typeof(ResizeThumb))); + } + + /// + /// Gets/Sets if the resize thumb is attached to the primary selection. + /// + public bool IsPrimarySelection { + get { return (bool)GetValue(IsPrimarySelectionProperty); } + set { SetValue(IsPrimarySelectionProperty, value); } + } + } +} diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/DesignPanel.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/DesignPanel.cs index df6e1cfd4d..d1659e3fea 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/DesignPanel.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/DesignPanel.cs @@ -140,7 +140,8 @@ namespace ICSharpCode.WpfDesign.Designer base.OnPreviewMouseDown(e); if (!_isInInputAction) { Debug.WriteLine("DesignPanel.PreviewMouseDown Source=" + e.Source.GetType().Name + " OriginalSource=" + e.OriginalSource.GetType().Name); - DesignItem site = FindDesignedElementForOriginalSource(e.OriginalSource); + object topMostHitSource = null; + DesignItem site = FindDesignedElementForOriginalSource(e.OriginalSource, ref topMostHitSource); InputHandlingLayer itemLayer = InputHandlingLayer.None; if (site != null) { Debug.WriteLine(" Found designed element: " + site.Component.GetType().Name); @@ -148,6 +149,8 @@ namespace ICSharpCode.WpfDesign.Designer if (layerProvider != null) { itemLayer = layerProvider.InputLayer; } + } else if (topMostHitSource == _adornerLayer) { + itemLayer = InputHandlingLayer.Adorners; } if (ToolService.CurrentTool.InputLayer > itemLayer) { ToolService.CurrentTool.OnMouseDown(this, e); @@ -155,7 +158,11 @@ namespace ICSharpCode.WpfDesign.Designer } } - public DesignItem FindDesignedElementForOriginalSource(object originalSource) + // find a DesignItem for the clicked originalSource. This walks up the visual tree until it finds a designed item + // or the design surface itself. + // topMostSource is set to the element directly below the one that caused the tree walk to abort. + // For hits on the adorner layer, the method will return null and set topMostSource to _adornerLayer. + DesignItem FindDesignedElementForOriginalSource(object originalSource, ref object topMostSource) { if (originalSource == null) return null; @@ -164,10 +171,17 @@ namespace ICSharpCode.WpfDesign.Designer return site; if (originalSource == this) return null; + topMostSource = originalSource; DependencyObject dObj = originalSource as DependencyObject; if (dObj == null) return null; - return FindDesignedElementForOriginalSource(VisualTreeHelper.GetParent(dObj)); + return FindDesignedElementForOriginalSource(VisualTreeHelper.GetParent(dObj), ref topMostSource); + } + + public DesignItem FindDesignedElementForOriginalSource(object originalSource) + { + object topMostSource = null; + return FindDesignedElementForOriginalSource(originalSource, ref topMostSource); } /// diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Extensions/SelectedElementRectangleExtension.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Extensions/SelectedElementRectangleExtension.cs index 05bb2b37f4..f69fb4e711 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Extensions/SelectedElementRectangleExtension.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Extensions/SelectedElementRectangleExtension.cs @@ -26,23 +26,25 @@ namespace ICSharpCode.WpfDesign.Designer.Extensions /// public SelectedElementRectangleExtension() { - Rectangle r = new Rectangle(); - r.SnapsToDevicePixels = true; - r.Stroke = Brushes.Black; - r.StrokeDashCap = PenLineCap.Square; - r.StrokeDashArray = new DoubleCollection(new double[] { 0, 2 }); - r.IsHitTestVisible = false; + Rectangle selectionRect = new Rectangle(); + selectionRect.SnapsToDevicePixels = true; + selectionRect.Stroke = Brushes.White; + selectionRect.IsHitTestVisible = false; - RelativePlacement placement = new RelativePlacement(); - placement.WidthRelativeToContentWidth = 1; - placement.HeightRelativeToContentHeight = 1; - placement.WidthOffset = 2; - placement.HeightOffset = 2; + Rectangle dottedRect = new Rectangle(); + dottedRect.SnapsToDevicePixels = true; + dottedRect.Stroke = Brushes.Black; + dottedRect.StrokeDashCap = PenLineCap.Square; + dottedRect.StrokeDashArray = new DoubleCollection(new double[] { 0, 2 }); + dottedRect.IsHitTestVisible = false; + RelativePlacement placement = new RelativePlacement(HorizontalAlignment.Stretch, VerticalAlignment.Stretch); placement.XOffset = -1; placement.YOffset = -1; + placement.WidthOffset = 2; + placement.HeightOffset = 2; - this.AddAdorner(r, placement); + this.AddAdorners(placement, selectionRect, dottedRect); } } } diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Extensions/TopLeftContainerDragHandle.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Extensions/TopLeftContainerDragHandle.cs new file mode 100644 index 0000000000..a343818e5b --- /dev/null +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Extensions/TopLeftContainerDragHandle.cs @@ -0,0 +1,42 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Windows.Controls; +using System.Windows; +using System.Windows.Media; +using System.Windows.Shapes; +using ICSharpCode.WpfDesign.Adorners; +using ICSharpCode.WpfDesign.Extensions; +using ICSharpCode.WpfDesign.Designer.Controls; + +namespace ICSharpCode.WpfDesign.Designer.Extensions +{ + /// + /// The drag handle displayed for panels. + /// + [ExtensionServer(typeof(PrimarySelectionExtensionServer))] + [ExtensionFor(typeof(Panel), OverrideExtension = typeof(TopLeftResizeThumb))] + public class TopLeftContainerDragHandle : AdornerProvider + { + /// + public TopLeftContainerDragHandle() + { + ContainerDragHandle rect = new ContainerDragHandle(); + + rect.PreviewMouseDown += delegate { + Services.Selection.SetSelectedComponents(new DesignItem[] { this.ExtendedItem }, SelectionTypes.Auto); + }; + + RelativePlacement p = new RelativePlacement(HorizontalAlignment.Left, VerticalAlignment.Top); + p.XOffset = -1; + p.YOffset = -1; + + AddAdorner(p, AdornerOrder.Background, rect); + } + } +} diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Extensions/TopLeftGrabHandle.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Extensions/TopLeftGrabHandle.cs new file mode 100644 index 0000000000..039e56394b --- /dev/null +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Extensions/TopLeftGrabHandle.cs @@ -0,0 +1,34 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Collections.Generic; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Controls.Primitives; +using ICSharpCode.WpfDesign.Adorners; +using ICSharpCode.WpfDesign.Extensions; +using ICSharpCode.WpfDesign.Designer.Controls; + +namespace ICSharpCode.WpfDesign.Designer.Extensions +{ + /// + /// The resize thumb at the top left edge of a component. + /// + [ExtensionFor(typeof(FrameworkElement))] + public class TopLeftResizeThumb : PrimarySelectionAdornerProvider + { + /// + public TopLeftResizeThumb() + { + ResizeThumb resizeThumb = new ResizeThumb(); + + RelativePlacement p = new RelativePlacement(HorizontalAlignment.Left, VerticalAlignment.Top); + AddAdorner(p, AdornerOrder.Foreground, resizeThumb); + } + } +} diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Services/UndoService.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Services/UndoService.cs new file mode 100644 index 0000000000..ea0474b0ef --- /dev/null +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Services/UndoService.cs @@ -0,0 +1,247 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Collections.Generic; +using System.Diagnostics; + +namespace ICSharpCode.WpfDesign.Designer.Services +{ + #region ITransactionItem + interface ITransactionItem + { + void Do(); + void Undo(); + + string Title { get; set; } + } + #endregion + + #region UndoTransaction + /// + /// Supports ChangeGroup transactions and undo behavior. + /// + sealed class UndoTransaction : ChangeGroup, ITransactionItem + { + public enum TransactionState + { + Open, + Completed, + Undone, + Failed + } + + TransactionState _state; + + public TransactionState State + { + get { return _state; } + } + + List items = new List(); + + public void Execute(ITransactionItem item) + { + AssertState(TransactionState.Open); + item.Do(); + items.Add(item); + } + + private void AssertState(TransactionState expectedState) + { + if (_state != expectedState) + throw new InvalidOperationException("Expected state " + expectedState + ", but state is " + _state); + } + + public event EventHandler Committed; + public event EventHandler RolledBack; + + public override void Commit() + { + AssertState(TransactionState.Open); + _state = TransactionState.Completed; + if (Committed != null) + Committed(this, EventArgs.Empty); + } + + public override void Abort() + { + AssertState(TransactionState.Open); + _state = TransactionState.Failed; + InternalRollback(); + if (RolledBack != null) + RolledBack(this, EventArgs.Empty); + } + + public void Undo() + { + AssertState(TransactionState.Completed); + _state = TransactionState.Undone; + InternalRollback(); + } + + void InternalRollback() + { + try { + for (int i = items.Count - 1; i >= 0; i--) { + items[i].Undo(); + } + } catch { + _state = TransactionState.Failed; + throw; + } + } + + public void Redo() + { + AssertState(TransactionState.Undone); + try { + for (int i = 0; i < items.Count; i--) { + items[i].Do(); + } + } catch { + _state = TransactionState.Failed; + try { + InternalRollback(); + } catch (Exception ex) { + Debug.WriteLine("Exception rolling back after Redo error:\n" + ex.ToString()); + } + throw; + } + } + + void ITransactionItem.Do() + { + if (_state != TransactionState.Completed) { + Redo(); + } + } + + protected override void Disposed() + { + if (_state == TransactionState.Open) { + try { + Abort(); + } catch (Exception ex) { + Debug.WriteLine("Exception rolling back after failure:\n" + ex.ToString()); + } + } + } + } + #endregion + + #region UndoService + /// + /// Service supporting Undo/Redo actions on the design surface. + /// + public sealed class UndoService + { + Stack _transactionStack = new Stack(); + 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(); + t.Committed += TransactionFinished; + t.RolledBack += TransactionFinished; + t.Committed += delegate(object sender, EventArgs e) { + Execute((UndoTransaction)sender); + }; + return t; + } + + void TransactionFinished(object sender, EventArgs e) + { + if (sender != _transactionStack.Pop()) { + throw new Exception("Invalid transaction finish, nested transactions must finish first"); + } + } + + internal void Execute(ITransactionItem item) + { + if (_transactionStack.Count == 0) { + item.Do(); + _undoStack.Push(item); + _redoStack.Clear(); + } else { + _transactionStack.Peek().Execute(item); + } + } + + /// + /// Gets if undo actions are available. + /// + public bool CanUndo { + get { return _undoStack.Count > 0; } + } + + /// + /// Undoes the last action. + /// + public void Undo() + { + if (!CanUndo) + throw new InvalidOperationException("Cannot Undo: undo stack is empty"); + if (_transactionStack.Count != 0) + throw new InvalidOperationException("Cannot Undo while transaction is running"); + ITransactionItem item = _undoStack.Pop(); + try { + item.Undo(); + _redoStack.Push(item); + } catch { + // state might be invalid now, clear stacks to prevent getting more inconsistencies + Clear(); + throw; + } + } + + /// + /// Gets if there are redo actions available. + /// + public bool CanRedo { get { return _redoStack.Count > 0; } } + + /// + /// Redoes a previously undone action. + /// + public void Redo() + { + if (!CanRedo) + throw new InvalidOperationException("Cannot Redo: redo stack is empty"); + if (_transactionStack.Count != 0) + throw new InvalidOperationException("Cannot Redo while transaction is running"); + ITransactionItem item = _redoStack.Pop(); + try { + item.Do(); + _undoStack.Push(item); + } catch { + // state might be invalid now, clear stacks to prevent getting more inconsistencies + Clear(); + throw; + } + } + + /// + /// Clears saved actions (both undo and redo stack). + /// + public void Clear() + { + _undoStack.Clear(); + _redoStack.Clear(); + } + } + #endregion +} 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 220c6c03b5..9f2eb4b6f6 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/WpfDesign.Designer.csproj +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/WpfDesign.Designer.csproj @@ -57,24 +57,32 @@ + + + + + + + + @@ -86,5 +94,6 @@ WpfDesign False + \ No newline at end of file diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Xaml/XamlComponentService.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Xaml/XamlComponentService.cs index 707d61d624..9cd00ed34c 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Xaml/XamlComponentService.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Xaml/XamlComponentService.cs @@ -38,6 +38,7 @@ namespace ICSharpCode.WpfDesign.Designer.Xaml { if (component == null) throw new ArgumentNullException("component"); + throw new NotImplementedException(); } 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 0de3e52ed4..8a1827d83a 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Xaml/XamlDesignContext.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Xaml/XamlDesignContext.cs @@ -13,12 +13,18 @@ using ICSharpCode.WpfDesign.Extensions; namespace ICSharpCode.WpfDesign.Designer.Xaml { - sealed class XamlDesignContext : DesignContext + /// + /// The design context implementation used when editing XAML. + /// + public sealed class XamlDesignContext : DesignContext { readonly XamlDocument _doc; readonly XamlDesignItem _rootItem; - readonly XamlComponentService _componentService; + internal readonly XamlComponentService _componentService; + /// + /// Creates a new XamlDesignContext instance. + /// public XamlDesignContext(XmlReader xamlReader) { if (xamlReader == null) @@ -27,6 +33,7 @@ namespace ICSharpCode.WpfDesign.Designer.Xaml this.Services.AddService(typeof(IVisualDesignService), new DefaultVisualDesignService()); this.Services.AddService(typeof(ISelectionService), new DefaultSelectionService()); this.Services.AddService(typeof(IToolService), new DefaultToolService()); + this.Services.AddService(typeof(UndoService), new UndoService()); _componentService = new XamlComponentService(this); this.Services.AddService(typeof(IComponentService), _componentService); @@ -51,11 +58,17 @@ namespace ICSharpCode.WpfDesign.Designer.Xaml return CustomInstanceFactory.DefaultInstanceFactory.CreateInstance(instanceType, arguments); } + /// + /// Saves the XAML DOM into the XML writer. + /// public override void Save(System.Xml.XmlWriter writer) { _doc.Save(writer); } + /// + /// Gets the root item being designed. + /// public override DesignItem RootItem { get { return _rootItem; } } diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Xaml/XamlDesignItem.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Xaml/XamlDesignItem.cs index be7fc80d08..d401fb2770 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Xaml/XamlDesignItem.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Xaml/XamlDesignItem.cs @@ -8,6 +8,7 @@ using System; using System.Windows; using ICSharpCode.WpfDesign.XamlDom; +using ICSharpCode.WpfDesign.Designer.Services; namespace ICSharpCode.WpfDesign.Designer.Xaml { @@ -15,12 +16,24 @@ namespace ICSharpCode.WpfDesign.Designer.Xaml { readonly XamlObject _xamlObject; readonly XamlDesignContext _designContext; + readonly XamlModelPropertyCollection _properties; UIElement _view; public XamlDesignItem(XamlObject xamlObject, XamlDesignContext designContext) { this._xamlObject = xamlObject; this._designContext = designContext; + this._properties = new XamlModelPropertyCollection(this); + } + + internal XamlComponentService ComponentService { + get { + return _designContext._componentService; + } + } + + internal XamlObject XamlObject { + get { return _xamlObject; } } public override object Component { @@ -29,6 +42,15 @@ namespace ICSharpCode.WpfDesign.Designer.Xaml } } + public override DesignItem Parent { + get { + if (_xamlObject.ParentProperty == null) + return null; + else + return ComponentService.GetDesignItem(_xamlObject.ParentProperty.ParentObject.Instance); + } + } + public override UIElement View { get { if (_view != null) @@ -38,6 +60,16 @@ namespace ICSharpCode.WpfDesign.Designer.Xaml } } + public override ChangeGroup OpenGroup(string changeGroupTitle) + { + UndoService undoService = this.Services.GetService(); + if (undoService == null) + throw new ServiceRequiredException(typeof(UndoService)); + UndoTransaction g = undoService.StartTransaction(); + g.Title = changeGroupTitle; + return g; + } + internal void SetView(UIElement newView) { _view = newView; @@ -46,5 +78,9 @@ namespace ICSharpCode.WpfDesign.Designer.Xaml public override DesignContext Context { get { return _designContext; } } + + public override DesignItemPropertyCollection Properties { + get { return _properties; } + } } } diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Xaml/XamlModelProperty.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Xaml/XamlModelProperty.cs new file mode 100644 index 0000000000..3d537c51bf --- /dev/null +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Xaml/XamlModelProperty.cs @@ -0,0 +1,85 @@ +// +// +// +// +// $Revision$ +// + +using System; +using ICSharpCode.WpfDesign.XamlDom; + +namespace ICSharpCode.WpfDesign.Designer.Xaml +{ + sealed class XamlModelProperty : DesignItemProperty + { + readonly XamlDesignItem _designItem; + readonly XamlProperty _property; + + public XamlModelProperty(XamlDesignItem designItem, XamlProperty property) + { + this._designItem = designItem; + this._property = property; + } + + public override bool IsCollection { + get { + return _property.IsCollection; + } + } + + public override System.Collections.Generic.IList CollectionElements { + get { + throw new NotImplementedException(); + } + } + + public override DesignItem Value { + get { + if (IsCollection) + throw new DesignerException("Cannot access Value for collection properties."); + + XamlComponentService componentService = _designItem.ComponentService; + object valueOnInstance = this.ValueOnInstance; + DesignItem designItem = componentService.GetDesignItem(valueOnInstance); + if (designItem != null) + return designItem; + + return componentService.RegisterComponentForDesigner(valueOnInstance); + } + } + + public override object ValueOnInstance { + get { + return _property.ValueOnInstance; + } + set { + _property.ValueOnInstance = value; + } + } + + public override bool IsSet { + get { + return _property.IsSet; + } + } + + public override void SetValue(object value) + { + XamlComponentService componentService = _designItem.ComponentService; + + DesignItem designItem = componentService.GetDesignItem(value); + if (designItem != null) { + if (designItem.Parent != null) + throw new DesignerException("Cannot set value to design item that already has a parent"); + } else { + designItem = componentService.RegisterComponentForDesigner(value); + } + + } + + public override void Reset() + { + _property.Reset(); + } + } +} diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Xaml/XamlModelPropertyCollection.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Xaml/XamlModelPropertyCollection.cs new file mode 100644 index 0000000000..465f94cd63 --- /dev/null +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Xaml/XamlModelPropertyCollection.cs @@ -0,0 +1,41 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Windows; +using ICSharpCode.WpfDesign.XamlDom; + +namespace ICSharpCode.WpfDesign.Designer.Xaml +{ + sealed class XamlModelPropertyCollection : DesignItemPropertyCollection + { + XamlDesignItem _item; + + public XamlModelPropertyCollection(XamlDesignItem item) + { + this._item = item; + } + + public override DesignItemProperty this[DependencyProperty dependencyProperty] { + get { + //return new XamlModelProperty(_item, dependencyProperty); + return this[dependencyProperty.Name]; + } + } + + public override DesignItemProperty this[string name] { + get { + return new XamlModelProperty(_item, _item.XamlObject.FindOrCreateProperty(name)); + } + } + + public override System.Collections.Generic.IEnumerator GetEnumerator() + { + throw new NotImplementedException(); + } + } +} diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/themes/generic.xaml b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/themes/generic.xaml new file mode 100644 index 0000000000..21cb58e3f7 --- /dev/null +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/themes/generic.xaml @@ -0,0 +1,49 @@ + + + + + + diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.XamlDom/Project/XamlObject.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.XamlDom/Project/XamlObject.cs index e68a980f05..2fc0db694d 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.XamlDom/Project/XamlObject.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.XamlDom/Project/XamlObject.cs @@ -7,6 +7,7 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.Xml; namespace ICSharpCode.WpfDesign.XamlDom @@ -74,5 +75,27 @@ namespace ICSharpCode.WpfDesign.XamlDom return properties.AsReadOnly(); } } + + /// + /// Finds the specified property, or creates it if it doesn't exist. + /// + public XamlProperty FindOrCreateProperty(string propertyName) + { + if (propertyName == null) + throw new ArgumentNullException("propertyName"); + + foreach (XamlProperty p in properties) { + if (p.PropertyName == propertyName) + return p; + } + PropertyDescriptorCollection propertyDescriptors = TypeDescriptor.GetProperties(elementType); + PropertyDescriptor propertyInfo = propertyDescriptors[propertyName]; + if (propertyInfo == null) { + throw new ArgumentException("The property '" + propertyName + "' doesn't exist on " + elementType.FullName, "propertyName"); + } + XamlProperty newProperty = new XamlProperty(this, new XamlNormalPropertyInfo(propertyInfo)); + properties.Add(newProperty); + return newProperty; + } } } diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.XamlDom/Project/XamlParser.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.XamlDom/Project/XamlParser.cs index d2ad8089a6..73cfce7dc9 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.XamlDom/Project/XamlParser.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.XamlDom/Project/XamlParser.cs @@ -175,7 +175,7 @@ namespace ICSharpCode.WpfDesign.XamlDom if (childValue != null) { if (defaultProperty != null && defaultProperty.IsCollection) { defaultProperty.AddValue(defaultPropertyValue, childValue); - defaultCollectionProperty.AddCollectionElement(childValue); + defaultCollectionProperty.ParserAddCollectionElement(childValue); } else { if (setDefaultValueTo != null) throw new XamlLoadException("default property may have only one value assigned"); @@ -335,7 +335,7 @@ namespace ICSharpCode.WpfDesign.XamlDom if (childValue != null) { if (propertyInfo.IsCollection) { propertyInfo.AddValue(collectionInstance, childValue); - collectionProperty.AddCollectionElement(childValue); + collectionProperty.ParserAddCollectionElement(childValue); } else { if (valueWasSet) throw new XamlLoadException("non-collection property may have only one child element"); diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.XamlDom/Project/XamlProperty.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.XamlDom/Project/XamlProperty.cs index 127fd0f395..4c39a186b6 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.XamlDom/Project/XamlProperty.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.XamlDom/Project/XamlProperty.cs @@ -8,6 +8,7 @@ using System; using System.Collections.Generic; using System.ComponentModel; +using System.Diagnostics; using System.Text; using System.Xml; @@ -21,13 +22,33 @@ namespace ICSharpCode.WpfDesign.XamlDom XamlObject parentObject; XamlPropertyInfo propertyInfo; XamlPropertyValue propertyValue; - List collectionElements = new List(); + List collectionElements; + bool _isCollection; + static readonly IList emptyCollectionElementsArray = new XamlPropertyValue[0]; + + // for use by parser only internal XamlProperty(XamlObject parentObject, XamlPropertyInfo propertyInfo, XamlPropertyValue propertyValue) { this.parentObject = parentObject; this.propertyInfo = propertyInfo; + this.propertyValue = propertyValue; + if (propertyValue != null) { + propertyValue.ParentProperty = this; + } else { + if (propertyInfo.IsCollection) { + _isCollection = true; + collectionElements = new List(); + } + } + } + + internal XamlProperty(XamlObject parentObject, XamlPropertyInfo propertyInfo) + { + this.parentObject = parentObject; + this.propertyInfo = propertyInfo; + _isCollection = propertyInfo.IsCollection; } /// @@ -51,20 +72,55 @@ namespace ICSharpCode.WpfDesign.XamlDom get { return propertyValue; } } + /// + /// Gets if the property is a collection property. + /// + public bool IsCollection { + get { return _isCollection; } + } + /// /// Gets the collection elements of the property. Is empty if the property is not a collection. /// public IList CollectionElements { - get { return collectionElements.AsReadOnly(); } + get { return collectionElements != null ? collectionElements.AsReadOnly() : emptyCollectionElementsArray; } + } + + /// + /// Gets if the property is set. + /// + public bool IsSet { + get { return propertyValue != null || collectionElements != null; } + } + + /// + /// Resets the properties value. + /// + public void Reset() + { + throw new NotImplementedException(); } /// /// used internally by the XamlParser. /// Add a collection element that already is part of the XML DOM. /// - internal void AddCollectionElement(XamlPropertyValue val) + internal void ParserAddCollectionElement(XamlPropertyValue val) { collectionElements.Add(val); + val.ParentProperty = this; + } + + /// + /// Gets/Sets the value of the property on the instance without updating the XAML document. + /// + public object ValueOnInstance { + get{ + return propertyInfo.GetValue(parentObject.Instance); + } + set { + propertyInfo.SetValue(parentObject.Instance, value); + } } /*public bool IsAttributeSyntax { @@ -96,6 +152,16 @@ namespace ICSharpCode.WpfDesign.XamlDom /// used internally by the XamlParser. /// internal abstract object GetValueFor(XamlPropertyInfo targetProperty); + + XamlProperty _parentProperty; + + /// + /// Gets the parent property that this value is assigned to. + /// + public XamlProperty ParentProperty { + get { return _parentProperty; } + internal set { _parentProperty = value; } + } } /// diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Adorners/AdornerProvider.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Adorners/AdornerProvider.cs index 7374cfe15f..21cf2ee181 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Adorners/AdornerProvider.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Adorners/AdornerProvider.cs @@ -116,14 +116,28 @@ namespace ICSharpCode.WpfDesign.Adorners /// /// Adds an UIElement as adorner with the specified placement. /// - protected void AddAdorner(UIElement adorner, Placement placement) + protected void AddAdorner(Placement placement, AdornerOrder order, UIElement adorner) { - AdornerPanel.SetPlacement(adorner, placement); AdornerPanel p = new AdornerPanel(); + p.Order = order; + AdornerPanel.SetPlacement(adorner, placement); p.Children.Add(adorner); this.Adorners.Add(p); } + /// + /// Adds several UIElements as adorners with the specified placement. + /// + protected void AddAdorners(Placement placement, params UIElement[] adorners) + { + AdornerPanel p = new AdornerPanel(); + foreach (UIElement adorner in adorners) { + AdornerPanel.SetPlacement(adorner, placement); + p.Children.Add(adorner); + } + this.Adorners.Add(p); + } + internal void OnAdornerAdd(AdornerPanel item) { if (!isVisible) return; diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Adorners/Placement.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Adorners/Placement.cs index 742ea73480..489a52e7ae 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Adorners/Placement.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Adorners/Placement.cs @@ -45,10 +45,61 @@ namespace ICSharpCode.WpfDesign.Adorners /// public sealed class RelativePlacement : Placement { + /// + /// Creates a new RelativePlacement instance. The default instance is a adorner with zero size, you + /// have to set some properties to define the placement. + /// + public RelativePlacement() + { + } + + /// + /// Creates a new RelativePlacement instance from the specified horizontal and vertical alignments. + /// + public RelativePlacement(HorizontalAlignment horizontal, VerticalAlignment vertical) + { + switch (horizontal) { + case HorizontalAlignment.Left: + widthRelativeToDesiredWidth = 1; + xRelativeToAdornerWidth = -1; + break; + case HorizontalAlignment.Right: + widthRelativeToDesiredWidth = 1; + xRelativeToContentWidth = 1; + break; + case HorizontalAlignment.Center: + widthRelativeToDesiredWidth = 1; + xRelativeToContentWidth = 0.5; + xRelativeToAdornerWidth = -0.5; + break; + case HorizontalAlignment.Stretch: + widthRelativeToContentWidth = 1; + break; + } + switch (vertical) { + case VerticalAlignment.Top: + heightRelativeToDesiredHeight = 1; + yRelativeToAdornerHeight = -1; + break; + case VerticalAlignment.Bottom: + heightRelativeToDesiredHeight = 1; + yRelativeToContentHeight = 1; + break; + case VerticalAlignment.Center: + heightRelativeToDesiredHeight = 1; + yRelativeToContentHeight = 0.5; + yRelativeToAdornerHeight = -0.5; + break; + case VerticalAlignment.Stretch: + heightRelativeToContentHeight = 1; + break; + } + } + double widthRelativeToDesiredWidth, heightRelativeToDesiredHeight; /// - /// Gets/Sets the width of the adorner relative to the desired adorner width. + /// Gets/Sets the width of the adorner as factor relative to the desired adorner width. /// public double WidthRelativeToDesiredWidth { get { return widthRelativeToDesiredWidth; } @@ -56,7 +107,7 @@ namespace ICSharpCode.WpfDesign.Adorners } /// - /// Gets/Sets the height of the adorner relative to the desired adorner height. + /// Gets/Sets the height of the adorner as factor relative to the desired adorner height. /// public double HeightRelativeToDesiredHeight { get { return heightRelativeToDesiredHeight; } @@ -66,7 +117,7 @@ namespace ICSharpCode.WpfDesign.Adorners double widthRelativeToContentWidth, heightRelativeToContentHeight; /// - /// Gets/Sets the width of the adorner relative to the width of the adorned item. + /// Gets/Sets the width of the adorner as factor relative to the width of the adorned item. /// public double WidthRelativeToContentWidth { get { return widthRelativeToContentWidth; } @@ -74,7 +125,7 @@ namespace ICSharpCode.WpfDesign.Adorners } /// - /// Gets/Sets the height of the adorner relative to the height of the adorned item. + /// Gets/Sets the height of the adorner as factor relative to the height of the adorned item. /// public double HeightRelativeToContentHeight { get { return heightRelativeToContentHeight; } @@ -101,14 +152,12 @@ namespace ICSharpCode.WpfDesign.Adorners Size CalculateSize(UIElement adorner, Size adornedElementSize) { - Size size = new Size(widthOffset, heightOffset); - if (widthRelativeToDesiredWidth != 0 || heightRelativeToDesiredHeight != 0) { - size.Width += widthRelativeToDesiredWidth * adorner.DesiredSize.Width; - size.Height += heightRelativeToDesiredHeight * adorner.DesiredSize.Height; - } - size.Width += widthRelativeToContentWidth * adornedElementSize.Width; - size.Height += heightRelativeToContentHeight * adornedElementSize.Height; - return size; + return new Size(widthOffset + + widthRelativeToDesiredWidth * adorner.DesiredSize.Width + + widthRelativeToContentWidth * adornedElementSize.Width, + heightOffset + + heightRelativeToDesiredHeight * adorner.DesiredSize.Height + + heightRelativeToContentHeight * adornedElementSize.Height); } double xOffset, yOffset; @@ -129,9 +178,50 @@ namespace ICSharpCode.WpfDesign.Adorners set { yOffset = value; } } + double xRelativeToAdornerWidth, yRelativeToAdornerHeight; + + /// + /// Gets/Sets the left border of the adorner element as factor relative to the width of the adorner. + /// + public double XRelativeToAdornerWidth { + get { return xRelativeToAdornerWidth; } + set { xRelativeToAdornerWidth = value; } + } + + /// + /// Gets/Sets the top border of the adorner element as factor relative to the height of the adorner. + /// + public double YRelativeToAdornerHeight { + get { return yRelativeToAdornerHeight; } + set { yRelativeToAdornerHeight = value; } + } + + double xRelativeToContentWidth, yRelativeToContentHeight; + + /// + /// Gets/Sets the left border of the adorner element as factor relative to the width of the adorned content. + /// + public double XRelativeToContentWidth { + get { return xRelativeToContentWidth; } + set { xRelativeToContentWidth = value; } + } + + /// + /// Gets/Sets the top border of the adorner element as factor relative to the height of the adorned content. + /// + public double YRelativeToContentHeight { + get { return yRelativeToContentHeight; } + set { yRelativeToContentHeight = value; } + } + Point CalculatePosition(Size adornedElementSize, Size adornerSize) { - return new Point(xOffset, yOffset); + return new Point(xOffset + + xRelativeToAdornerWidth * adornerSize.Width + + xRelativeToContentWidth * adornedElementSize.Width, + yOffset + + yRelativeToAdornerHeight * adornerSize.Height + + yRelativeToContentHeight * adornedElementSize.Height); } /// @@ -143,7 +233,7 @@ namespace ICSharpCode.WpfDesign.Adorners adorner.Arrange(new Rect(CalculatePosition(adornedElementSize, adornerSize), adornerSize)); } } - + /// /// Describes the space in which an adorner is placed. /// @@ -162,32 +252,4 @@ namespace ICSharpCode.WpfDesign.Adorners /// Designer } - - /// - /// The possible layers where adorners can be placed. - /// - public enum AdornerZLayer - { - /// - /// This layer is below the other adorner layers. - /// - Low, - /// - /// This layer is for normal background adorners. - /// - Normal, - /// - /// This layer is for selection adorners - /// - Selection, - /// - /// This layer is for primary selection adorners - /// - PrimarySelection, - /// - /// This layer is above the other layers. - /// It is used for temporary drawings, e.g. the selection frame while selecting multiple controls with the mouse. - /// - High - } } diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/ChangeGroup.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/ChangeGroup.cs new file mode 100644 index 0000000000..02c0f6faf0 --- /dev/null +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/ChangeGroup.cs @@ -0,0 +1,47 @@ +// +// +// +// +// $Revision$ +// + +using System; + +namespace ICSharpCode.WpfDesign +{ + /// + /// Base class for change groups. + /// + public abstract class ChangeGroup : IDisposable + { + string title; + + /// + /// Gets/Sets the title of the change group. + /// + public string Title { + get { return title; } + set { title = value; } + } + + /// + /// Commits the change group. + /// + public abstract void Commit(); + + /// + /// Aborts the change group. + /// + public abstract void Abort(); + + /// + /// Is called when the change group is disposed. Should Abort the change group if it is not already committed. + /// + protected abstract void Disposed(); + + void IDisposable.Dispose() + { + Disposed(); + } + } +} diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/DesignItem.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/DesignItem.cs index 504a3205d4..ca1f38dec6 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/DesignItem.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/DesignItem.cs @@ -34,6 +34,16 @@ namespace ICSharpCode.WpfDesign /// public abstract DesignContext Context { get; } + /// + /// Gets the parent design item. + /// + public abstract DesignItem Parent { get; } + + /// + /// Gets properties set on the design item. + /// + public abstract DesignItemPropertyCollection Properties { get; } + /// /// Gets an instance that provides convenience properties for the most-used designers. /// @@ -42,6 +52,12 @@ namespace ICSharpCode.WpfDesign get { return this.Context.Services; } } + /// + /// Opens a new change group used to batch several changes. ChangeGroups work as transactions and + /// are used to support the Undo/Redo system. + /// + public abstract ChangeGroup OpenGroup(string changeGroupTitle); + #region Extensions support private struct ExtensionEntry { diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/DesignItemProperty.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/DesignItemProperty.cs new file mode 100644 index 0000000000..678ecc18e9 --- /dev/null +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/DesignItemProperty.cs @@ -0,0 +1,86 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Collections.ObjectModel; +using System.Collections.Generic; +using System.Windows; + +namespace ICSharpCode.WpfDesign +{ + /// + /// Represents a property of a DesignItem. + /// All changes done via the DesignItemProperty class are represented in the underlying model (e.g. XAML). + /// This also ensures that + /// Changes directly done to control instances might not be reflected in the model. + /// + public abstract class DesignItemProperty + { + /// + /// Gets if the property represents a collection. + /// + public abstract bool IsCollection { get; } + + /// + /// Gets the elements represented by the collection. + /// + public abstract IList CollectionElements { get; } + + /// + /// Gets the value of the property. This property returns null if the value is not set. + /// + public abstract DesignItem Value { get; } + + /// + /// Gets/Sets the value of the property on the designed instance. + /// If the property is not set, this returns the default value. + /// The setter does NOT update the underlying model, use SetValue() instead! + /// + public abstract object ValueOnInstance { get; set; } + + /// + /// Sets the value of the property. + /// + public abstract void SetValue(object value); + + /// + /// Gets if the property is set on the design item. + /// + public abstract bool IsSet { get; } + + /// + /// Resets the property value to the default, possibly removing it from the list of properties. + /// + public abstract void Reset(); + } + + /// + /// Represents a collection of design item properties. + /// + public abstract class DesignItemPropertyCollection : IEnumerable + { + /// + /// Gets the design item property representing the specified dependency property. + /// + public abstract DesignItemProperty this[DependencyProperty dependencyProperty] { get; } + + /// + /// Gets the property with the specified name. + /// + public abstract DesignItemProperty this[string name] { get; } + + /// + /// Gets an enumerator to enumerate the properties that have a non-default value. + /// + public abstract IEnumerator GetEnumerator(); + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return this.GetEnumerator(); + } + } +} diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Extensions/CustomInstanceFactory.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Extensions/CustomInstanceFactory.cs index b4465d87ee..01a97c340b 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Extensions/CustomInstanceFactory.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Extensions/CustomInstanceFactory.cs @@ -59,5 +59,10 @@ namespace ICSharpCode.WpfDesign.Extensions { throw new NotImplementedException(); } + + // since the event is never raised, we don't have to store the event handlers + public override event EventHandler ShouldApplyExtensionsInvalidated { + add {} remove {} + } } } diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Extensions/DefaultExtension.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Extensions/DefaultExtension.cs index 5194230e95..b5890bad23 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Extensions/DefaultExtension.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Extensions/DefaultExtension.cs @@ -7,6 +7,7 @@ using System; using System.Diagnostics; +using System.Collections.Generic; namespace ICSharpCode.WpfDesign.Extensions { @@ -103,6 +104,21 @@ namespace ICSharpCode.WpfDesign.Extensions ((DefaultExtension)extension).CallOnRemove(); } + /// + /// This event is raised when ShouldApplyExtensions is invalidated for a set of items. + /// + public override event EventHandler ShouldApplyExtensionsInvalidated; + + /// + /// Raise the ShouldApplyExtensionsInvalidated event for the specified set of design items. + /// + protected void ReapplyExtensions(ICollection items) + { + if (ShouldApplyExtensionsInvalidated != null) { + ShouldApplyExtensionsInvalidated(this, new DesignItemCollectionEventArgs(items)); + } + } + internal sealed class Permanent : DefaultExtensionServer { public override bool ShouldApplyExtensions(DesignItem extendedItem) diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Extensions/ExtensionManager.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Extensions/ExtensionManager.cs index 95bad14414..0a0a49fedc 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Extensions/ExtensionManager.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Extensions/ExtensionManager.cs @@ -38,7 +38,7 @@ namespace ICSharpCode.WpfDesign.Extensions /// /// Re-applies extensions from the ExtensionServer to the specified design items. /// - public void ReapplyExtensions(IEnumerable items, ExtensionServer server) + void ReapplyExtensions(IEnumerable items, ExtensionServer server) { foreach (DesignItem item in items) { item.ReapplyExtensionServer(this, server); @@ -203,6 +203,9 @@ namespace ICSharpCode.WpfDesign.Extensions server = (ExtensionServer)Activator.CreateInstance(extensionServerType); server.InitializeExtensionServer(_context); _extensionServers[extensionServerType] = server; + server.ShouldApplyExtensionsInvalidated += delegate(object sender, DesignItemCollectionEventArgs e) { + ReapplyExtensions(e.Items, (ExtensionServer)sender); + }; return server; } #endregion diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Extensions/ExtensionServer.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Extensions/ExtensionServer.cs index 220644f910..c247c488b3 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Extensions/ExtensionServer.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Extensions/ExtensionServer.cs @@ -74,5 +74,10 @@ namespace ICSharpCode.WpfDesign.Extensions /// Is called by the ExtensionManager. /// public abstract void RemoveExtension(Extension extension); + + /// + /// This event is raised when ShouldApplyExtensions is invalidated for a set of items. + /// + public abstract event EventHandler ShouldApplyExtensionsInvalidated; } } diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Extensions/LogicalExtensionServers.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Extensions/LogicalExtensionServers.cs new file mode 100644 index 0000000000..89e3ffd39b --- /dev/null +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Extensions/LogicalExtensionServers.cs @@ -0,0 +1,75 @@ +// +// +// +// +// $Revision$ +// + +using System; + +namespace ICSharpCode.WpfDesign.Extensions +{ + /// + /// Combines two extension servers using a logical OR. + /// + public sealed class LogicalOrExtensionServer : DefaultExtensionServer + where A : ExtensionServer + where B : ExtensionServer + { + ExtensionServer _a; + ExtensionServer _b; + + /// + protected override void OnInitialized() + { + base.OnInitialized(); + _a = Context.Services.ExtensionManager.GetExtensionServer(new ExtensionServerAttribute(typeof(A))); + _b = Context.Services.ExtensionManager.GetExtensionServer(new ExtensionServerAttribute(typeof(B))); + _a.ShouldApplyExtensionsInvalidated += OnShouldApplyExtensionsInvalidated; + _b.ShouldApplyExtensionsInvalidated += OnShouldApplyExtensionsInvalidated; + } + + void OnShouldApplyExtensionsInvalidated(object sender, DesignItemCollectionEventArgs e) + { + ReapplyExtensions(e.Items); + } + + /// + public override bool ShouldApplyExtensions(DesignItem extendedItem) + { + return _a.ShouldApplyExtensions(extendedItem) || _b.ShouldApplyExtensions(extendedItem); + } + } + + /// + /// Combines two extension servers using a logical AND. + /// + public sealed class LogicalAndExtensionServer : DefaultExtensionServer + where A : ExtensionServer + where B : ExtensionServer + { + ExtensionServer _a; + ExtensionServer _b; + + /// + protected override void OnInitialized() + { + base.OnInitialized(); + _a = Context.Services.ExtensionManager.GetExtensionServer(new ExtensionServerAttribute(typeof(A))); + _b = Context.Services.ExtensionManager.GetExtensionServer(new ExtensionServerAttribute(typeof(B))); + _a.ShouldApplyExtensionsInvalidated += OnShouldApplyExtensionsInvalidated; + _b.ShouldApplyExtensionsInvalidated += OnShouldApplyExtensionsInvalidated; + } + + void OnShouldApplyExtensionsInvalidated(object sender, DesignItemCollectionEventArgs e) + { + ReapplyExtensions(e.Items); + } + + /// + public override bool ShouldApplyExtensions(DesignItem extendedItem) + { + return _a.ShouldApplyExtensions(extendedItem) && _b.ShouldApplyExtensions(extendedItem); + } + } +} diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Extensions/SelectionExtensionServer.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Extensions/SelectionExtensionServer.cs index 76a602edc9..0a1b9b0d64 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Extensions/SelectionExtensionServer.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Extensions/SelectionExtensionServer.cs @@ -25,7 +25,7 @@ namespace ICSharpCode.WpfDesign.Extensions void OnSelectionChanged(object sender, DesignItemCollectionEventArgs e) { - Services.ExtensionManager.ReapplyExtensions(e.Items, this); + ReapplyExtensions(e.Items); } /// @@ -72,11 +72,11 @@ namespace ICSharpCode.WpfDesign.Extensions DesignItem newPrimarySelection = this.Services.Selection.PrimarySelection; if (oldPrimarySelection != newPrimarySelection) { if (oldPrimarySelection == null) { - this.Services.ExtensionManager.ReapplyExtensions(new DesignItem[] { newPrimarySelection }, this); + ReapplyExtensions(new DesignItem[] { newPrimarySelection }); } else if (newPrimarySelection == null) { - this.Services.ExtensionManager.ReapplyExtensions(new DesignItem[] { oldPrimarySelection }, this); + ReapplyExtensions(new DesignItem[] { oldPrimarySelection }); } else { - this.Services.ExtensionManager.ReapplyExtensions(new DesignItem[] { oldPrimarySelection, newPrimarySelection }, this); + ReapplyExtensions(new DesignItem[] { oldPrimarySelection, newPrimarySelection }); } oldPrimarySelection = newPrimarySelection; } @@ -90,4 +90,56 @@ namespace ICSharpCode.WpfDesign.Extensions return Services.Selection.PrimarySelection == extendedItem; } } + + /// + /// Applies an extension to the parent of the primary selection. + /// + public class PrimarySelectionParentExtensionServer : DefaultExtensionServer + { + DesignItem oldPrimarySelectionParent; + + /// + /// Is called after the extension server is initialized and the Context property has been set. + /// + protected override void OnInitialized() + { + base.OnInitialized(); + this.Services.Selection.PrimarySelectionChanged += OnPrimarySelectionChanged; + } + + DesignItem PrimarySelectionParent { + get { + DesignItem newPrimarySelection = this.Services.Selection.PrimarySelection; + if (newPrimarySelection != null) { + return newPrimarySelection.Parent; + } else { + return null; + } + } + } + + void OnPrimarySelectionChanged(object sender, EventArgs e) + { + DesignItem newPrimarySelectionParent = PrimarySelectionParent; + + if (oldPrimarySelectionParent != newPrimarySelectionParent) { + if (oldPrimarySelectionParent == null) { + ReapplyExtensions(new DesignItem[] { newPrimarySelectionParent }); + } else if (newPrimarySelectionParent == null) { + ReapplyExtensions(new DesignItem[] { oldPrimarySelectionParent }); + } else { + ReapplyExtensions(new DesignItem[] { oldPrimarySelectionParent, newPrimarySelectionParent }); + } + oldPrimarySelectionParent = newPrimarySelectionParent; + } + } + + /// + /// Gets if the item is the primary selection. + /// + public override bool ShouldApplyExtensions(DesignItem extendedItem) + { + return PrimarySelectionParent == extendedItem; + } + } } diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Tools.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Tools.cs index dbd10d7700..d998a3597c 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Tools.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Tools.cs @@ -38,6 +38,10 @@ namespace ICSharpCode.WpfDesign /// ComponentHigh, /// + /// This value is used for mouse input on the adorner layer. + /// + Adorners, + /// /// This layer is higher than all other layers. /// Highest @@ -131,7 +135,7 @@ namespace ICSharpCode.WpfDesign /// Used for temporary drawings that are not attached to any element, e.g. the selection frame. /// Canvas MarkerCanvas { get; } - */ + */ // The following members were missing in , but of course // are supported on the DesignPanel: diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/WpfDesign.csproj b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/WpfDesign.csproj index 3274ed8bee..c79b466756 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/WpfDesign.csproj +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/WpfDesign.csproj @@ -61,7 +61,9 @@ + + @@ -73,6 +75,7 @@ +