Browse Source

WpfDesigner: support dragging multiple selected components.

git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@2420 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61
shortcuts
Daniel Grunwald 19 years ago
parent
commit
2990725962
  1. 66
      src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Extensions/CanvasChildResizeSupport.cs
  2. 64
      src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Extensions/ResizeThumbExtension.cs
  3. 41
      src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Extensions/WindowResizeBehavior.cs
  4. 30
      src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Services/DragMoveMouseGesture.cs
  5. 18
      src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Xaml/XamlDesignContext.cs
  6. 10
      src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Xaml/XamlDesignItem.cs
  7. 116
      src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Xaml/XamlModelCollectionElementsCollection.cs
  8. 7
      src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/DesignContext.cs
  9. 11
      src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/DesignItem.cs
  10. 38
      src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Func.cs
  11. 32
      src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/PlacementBehavior.cs
  12. 58
      src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/PlacementInformation.cs
  13. 90
      src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/PlacementOperation.cs
  14. 1
      src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/WpfDesign.csproj

66
src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Extensions/CanvasChildResizeSupport.cs

@ -6,6 +6,7 @@
// </file> // </file>
using System; using System;
using System.Collections.Generic;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
@ -31,21 +32,16 @@ namespace ICSharpCode.WpfDesign.Designer.Extensions
GrayOutDesignerExceptActiveArea grayOut; GrayOutDesignerExceptActiveArea grayOut;
/// <inherits/> /// <inherits/>
public bool CanPlace(DesignItem child, PlacementType type, PlacementAlignment position) public bool CanPlace(ICollection<DesignItem> child, PlacementType type, PlacementAlignment position)
{ {
return type == PlacementType.Resize || type == PlacementType.Move; return type == PlacementType.Resize || type == PlacementType.Move;
} }
/// <inherits/> /// <inherits/>
public void StartPlacement(PlacementOperation operation) public Rect GetPosition(PlacementOperation operation, DesignItem childItem)
{ {
UIElement child = (UIElement)operation.PlacedItem.Component; UIElement child = childItem.View;
operation.Left = GetLeft(child); return new Rect(GetLeft(child), GetTop(child), GetWidth(child), GetHeight(child));
operation.Top = GetTop(child);
operation.Right = operation.Left + GetWidth(child);
operation.Bottom = operation.Top + GetHeight(child);
BeginPlacement();
} }
static double GetLeft(UIElement element) static double GetLeft(UIElement element)
@ -85,36 +81,32 @@ namespace ICSharpCode.WpfDesign.Designer.Extensions
} }
/// <inherits/> /// <inherits/>
public void UpdatePlacement(PlacementOperation operation) public void SetPosition(PlacementInformation info)
{ {
DesignItem item = operation.PlacedItem; UIElement child = info.Item.View;
UIElement child = (UIElement)item.Component; Rect newPosition = info.Bounds;
if (operation.Left != GetLeft(child)) { if (newPosition.Left != GetLeft(child)) {
item.Properties.GetAttachedProperty(Canvas.LeftProperty).SetValue(operation.Left); info.Item.Properties.GetAttachedProperty(Canvas.LeftProperty).SetValue(newPosition.Left);
} }
if (operation.Top != GetTop(child)) { if (newPosition.Top != GetTop(child)) {
item.Properties.GetAttachedProperty(Canvas.TopProperty).SetValue(operation.Top); info.Item.Properties.GetAttachedProperty(Canvas.TopProperty).SetValue(newPosition.Top);
} }
if (operation.Right - operation.Left != GetWidth(child)) { if (newPosition.Width != GetWidth(child)) {
item.Properties.GetProperty(FrameworkElement.WidthProperty).SetValue(operation.Right - operation.Left); info.Item.Properties.GetProperty(FrameworkElement.WidthProperty).SetValue(newPosition.Right - newPosition.Left);
} }
if (operation.Bottom - operation.Top != GetHeight(child)) { if (newPosition.Height != GetHeight(child)) {
item.Properties.GetProperty(FrameworkElement.HeightProperty).SetValue(operation.Bottom - operation.Top); info.Item.Properties.GetProperty(FrameworkElement.HeightProperty).SetValue(newPosition.Bottom - newPosition.Top);
} }
} }
/// <inherits/> /// <inherits/>
public void FinishPlacement(PlacementOperation operation) public void BeginPlacement(PlacementOperation op)
{
EndPlacement();
}
void BeginPlacement()
{ {
GrayOutDesignerExceptActiveArea.Start(ref grayOut, this.Services, this.ExtendedItem.View); GrayOutDesignerExceptActiveArea.Start(ref grayOut, this.Services, this.ExtendedItem.View);
} }
void EndPlacement() /// <inherits/>
public void EndPlacement(PlacementOperation op)
{ {
GrayOutDesignerExceptActiveArea.Stop(ref grayOut); GrayOutDesignerExceptActiveArea.Stop(ref grayOut);
} }
@ -128,11 +120,12 @@ namespace ICSharpCode.WpfDesign.Designer.Extensions
/// <inherits/> /// <inherits/>
public void LeaveContainer(PlacementOperation operation) public void LeaveContainer(PlacementOperation operation)
{ {
EndPlacement(); EndPlacement(operation);
foreach (PlacementInformation info in operation.PlacedItems) {
this.ExtendedItem.Properties["Children"].CollectionElements.Remove(operation.PlacedItem); this.ExtendedItem.Properties["Children"].CollectionElements.Remove(info.Item);
operation.PlacedItem.Properties.GetAttachedProperty(Canvas.LeftProperty).Reset(); info.Item.Properties.GetAttachedProperty(Canvas.LeftProperty).Reset();
operation.PlacedItem.Properties.GetAttachedProperty(Canvas.TopProperty).Reset(); info.Item.Properties.GetAttachedProperty(Canvas.TopProperty).Reset();
}
} }
/// <inherits/> /// <inherits/>
@ -144,10 +137,11 @@ namespace ICSharpCode.WpfDesign.Designer.Extensions
/// <inherits/> /// <inherits/>
public void EnterContainer(PlacementOperation operation) public void EnterContainer(PlacementOperation operation)
{ {
this.ExtendedItem.Properties["Children"].CollectionElements.Add(operation.PlacedItem); foreach (PlacementInformation info in operation.PlacedItems) {
UpdatePlacement(operation); this.ExtendedItem.Properties["Children"].CollectionElements.Add(info.Item);
SetPosition(info);
BeginPlacement(); }
BeginPlacement(operation);
} }
} }
} }

64
src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Extensions/ResizeThumbExtension.cs

@ -25,6 +25,8 @@ namespace ICSharpCode.WpfDesign.Designer.Extensions
{ {
readonly AdornerPanel adornerPanel; readonly AdornerPanel adornerPanel;
readonly ResizeThumb[] resizeThumbs; readonly ResizeThumb[] resizeThumbs;
/// <summary>An array containing this.ExtendedItem as only element</summary>
readonly DesignItem[] extendedItemArray = new DesignItem[1];
IPlacementBehavior resizeBehavior; IPlacementBehavior resizeBehavior;
PlacementOperation operation; PlacementOperation operation;
ResizeThumb activeResizeThumb; ResizeThumb activeResizeThumb;
@ -65,38 +67,45 @@ namespace ICSharpCode.WpfDesign.Designer.Extensions
void OnDragStarted(object sender, DragStartedEventArgs e) void OnDragStarted(object sender, DragStartedEventArgs e)
{ {
activeResizeThumb = (ResizeThumb)sender; activeResizeThumb = (ResizeThumb)sender;
operation = PlacementOperation.Start(this.ExtendedItem, PlacementType.Resize); operation = PlacementOperation.Start(extendedItemArray, PlacementType.Resize);
this.ExtendedItem.Services.GetService<IDesignPanel>().KeyDown += OnDesignPanelKeyDown; this.ExtendedItem.Services.GetService<IDesignPanel>().KeyDown += OnDesignPanelKeyDown;
} }
DragDeltaEventHandler OnDragDelta(PlacementAlignment alignment) DragDeltaEventHandler OnDragDelta(PlacementAlignment alignment)
{ {
return delegate(object sender, DragDeltaEventArgs e) { return delegate(object sender, DragDeltaEventArgs e) {
switch (alignment.Horizontal) { foreach (PlacementInformation info in operation.PlacedItems) {
case HorizontalAlignment.Left: double left = info.Bounds.Left;
operation.Left += e.HorizontalChange; double right = info.Bounds.Right;
if (operation.Left > operation.Right) double bottom = info.Bounds.Bottom;
operation.Left = operation.Right; double top = info.Bounds.Top;
break; switch (alignment.Horizontal) {
case HorizontalAlignment.Right: case HorizontalAlignment.Left:
operation.Right += e.HorizontalChange; left += e.HorizontalChange;
if (operation.Right < operation.Left) if (left > right)
operation.Right = operation.Left; left = right;
break; break;
case HorizontalAlignment.Right:
right += e.HorizontalChange;
if (right < left)
right = left;
break;
}
switch (alignment.Vertical) {
case VerticalAlignment.Top:
top += e.VerticalChange;
if (top > bottom)
top = bottom;
break;
case VerticalAlignment.Bottom:
bottom += e.VerticalChange;
if (bottom < top)
bottom = top;
break;
}
info.Bounds = new Rect(left, top, right - left, bottom - top);
operation.CurrentContainerBehavior.SetPosition(info);
} }
switch (alignment.Vertical) {
case VerticalAlignment.Top:
operation.Top += e.VerticalChange;
if (operation.Top > operation.Bottom)
operation.Top = operation.Bottom;
break;
case VerticalAlignment.Bottom:
operation.Bottom += e.VerticalChange;
if (operation.Bottom < operation.Top)
operation.Bottom = operation.Top;
break;
}
operation.CurrentContainerBehavior.UpdatePlacement(operation);
}; };
} }
@ -123,10 +132,11 @@ namespace ICSharpCode.WpfDesign.Designer.Extensions
protected override void OnInitialized() protected override void OnInitialized()
{ {
base.OnInitialized(); base.OnInitialized();
extendedItemArray[0] = this.ExtendedItem;
this.ExtendedItem.PropertyChanged += OnPropertyChanged; this.ExtendedItem.PropertyChanged += OnPropertyChanged;
this.Services.Selection.PrimarySelectionChanged += OnPrimarySelectionChanged; this.Services.Selection.PrimarySelectionChanged += OnPrimarySelectionChanged;
resizeBehavior = PlacementOperation.GetPlacementBehavior(this.ExtendedItem); resizeBehavior = PlacementOperation.GetPlacementBehavior(extendedItemArray);
UpdateAdornerVisibility(); UpdateAdornerVisibility();
OnPrimarySelectionChanged(null, null); OnPrimarySelectionChanged(null, null);
@ -157,7 +167,7 @@ namespace ICSharpCode.WpfDesign.Designer.Extensions
{ {
FrameworkElement fe = this.ExtendedItem.View as FrameworkElement; FrameworkElement fe = this.ExtendedItem.View as FrameworkElement;
foreach (ResizeThumb r in resizeThumbs) { foreach (ResizeThumb r in resizeThumbs) {
bool isVisible = resizeBehavior != null && resizeBehavior.CanPlace(this.ExtendedItem, PlacementType.Resize, r.Alignment); bool isVisible = resizeBehavior != null && resizeBehavior.CanPlace(extendedItemArray, PlacementType.Resize, r.Alignment);
r.Visibility = isVisible ? Visibility.Visible : Visibility.Hidden; r.Visibility = isVisible ? Visibility.Visible : Visibility.Hidden;
} }
} }

41
src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Extensions/WindowResizeBehavior.cs

@ -6,6 +6,7 @@
// </file> // </file>
using System; using System;
using System.Collections.Generic;
using System.Windows; using System.Windows;
using ICSharpCode.WpfDesign.Extensions; using ICSharpCode.WpfDesign.Extensions;
@ -25,7 +26,7 @@ namespace ICSharpCode.WpfDesign.Designer.Extensions
} }
/// <inherits/> /// <inherits/>
public bool CanPlace(DesignItem child, PlacementType type, PlacementAlignment position) public bool CanPlace(ICollection<DesignItem> children, PlacementType type, PlacementAlignment position)
{ {
return type == PlacementType.Resize && return type == PlacementType.Resize &&
(position == PlacementAlignments.Right (position == PlacementAlignments.Right
@ -33,14 +34,22 @@ namespace ICSharpCode.WpfDesign.Designer.Extensions
|| position == PlacementAlignments.Bottom); || position == PlacementAlignments.Bottom);
} }
/// <inherits/> /// <inherits/>
public void StartPlacement(PlacementOperation operation) public void BeginPlacement(PlacementOperation operation)
{ {
UIElement element = (UIElement)operation.PlacedItem.Component; }
operation.Left = 0;
operation.Top = 0; /// <inherits/>
operation.Right = GetWidth(element); public void EndPlacement(PlacementOperation operation)
operation.Bottom = GetHeight(element); {
}
/// <inherits/>
public Rect GetPosition(PlacementOperation operation, DesignItem childItem)
{
UIElement child = childItem.View;
return new Rect(0, 0, GetWidth(child), GetHeight(child));
} }
static double GetWidth(UIElement element) static double GetWidth(UIElement element)
@ -62,22 +71,18 @@ namespace ICSharpCode.WpfDesign.Designer.Extensions
} }
/// <inherits/> /// <inherits/>
public void UpdatePlacement(PlacementOperation operation) public void SetPosition(PlacementInformation info)
{ {
UIElement element = (UIElement)operation.PlacedItem.Component; UIElement element = info.Item.View;
if (operation.Right != GetWidth(element)) { Rect newPosition = info.Bounds;
operation.PlacedItem.Properties[FrameworkElement.WidthProperty].SetValue(operation.Right); if (newPosition.Right != GetWidth(element)) {
info.Item.Properties[FrameworkElement.WidthProperty].SetValue(newPosition.Right);
} }
if (operation.Bottom != GetHeight(element)) { if (newPosition.Bottom != GetHeight(element)) {
operation.PlacedItem.Properties[FrameworkElement.HeightProperty].SetValue(operation.Bottom); info.Item.Properties[FrameworkElement.HeightProperty].SetValue(newPosition.Bottom);
} }
} }
/// <inherits/>
public void FinishPlacement(PlacementOperation operation)
{
}
/// <inherits/> /// <inherits/>
public bool CanLeaveContainer(PlacementOperation operation) public bool CanLeaveContainer(PlacementOperation operation)
{ {

30
src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Services/DragMoveMouseGesture.cs

@ -6,6 +6,7 @@
// </file> // </file>
using System; using System;
using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Windows; using System.Windows;
using System.Windows.Input; using System.Windows.Input;
@ -20,6 +21,7 @@ namespace ICSharpCode.WpfDesign.Designer.Services
{ {
DesignItem clickedOn; DesignItem clickedOn;
PlacementOperation operation; PlacementOperation operation;
ICollection<DesignItem> selectedItems;
internal DragMoveMouseGesture(DesignItem clickedOn) internal DragMoveMouseGesture(DesignItem clickedOn)
{ {
@ -31,19 +33,17 @@ namespace ICSharpCode.WpfDesign.Designer.Services
this.positionRelativeTo = clickedOn.Parent.View; this.positionRelativeTo = clickedOn.Parent.View;
else else
this.positionRelativeTo = clickedOn.View; this.positionRelativeTo = clickedOn.View;
selectedItems = clickedOn.Services.Selection.SelectedItems;
if (!selectedItems.Contains(clickedOn))
selectedItems = new DesignItem[0];
} }
double startLeft, startRight, startTop, startBottom;
protected override void OnDragStarted() protected override void OnDragStarted()
{ {
IPlacementBehavior b = PlacementOperation.GetPlacementBehavior(clickedOn); IPlacementBehavior b = PlacementOperation.GetPlacementBehavior(selectedItems);
if (b != null && b.CanPlace(clickedOn, PlacementType.Move, PlacementAlignments.TopLeft)) { if (b != null && b.CanPlace(selectedItems, PlacementType.Move, PlacementAlignments.TopLeft)) {
operation = PlacementOperation.Start(clickedOn, PlacementType.Move); operation = PlacementOperation.Start(selectedItems, PlacementType.Move);
startLeft = operation.Left;
startRight = operation.Right;
startTop = operation.Top;
startBottom = operation.Bottom;
} }
} }
@ -63,11 +63,13 @@ namespace ICSharpCode.WpfDesign.Designer.Services
} }
Vector v = e.GetPosition(positionRelativeTo) - startPoint; Vector v = e.GetPosition(positionRelativeTo) - startPoint;
operation.Left = startLeft + v.X; foreach (PlacementInformation info in operation.PlacedItems) {
operation.Right = startRight + v.X; info.Bounds = new Rect(info.OriginalBounds.Left + v.X,
operation.Top = startTop + v.Y; info.OriginalBounds.Top + v.Y,
operation.Bottom = startBottom + v.Y; info.OriginalBounds.Width,
operation.CurrentContainerBehavior.UpdatePlacement(operation); info.OriginalBounds.Height);
operation.CurrentContainerBehavior.SetPosition(info);
}
} }
} }

18
src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Xaml/XamlDesignContext.cs

@ -6,6 +6,7 @@
// </file> // </file>
using System; using System;
using System.Collections.Generic;
using System.Xml; using System.Xml;
using ICSharpCode.WpfDesign.XamlDom; using ICSharpCode.WpfDesign.XamlDom;
using ICSharpCode.WpfDesign.Designer.Services; using ICSharpCode.WpfDesign.Designer.Services;
@ -84,5 +85,22 @@ namespace ICSharpCode.WpfDesign.Designer.Xaml
public override DesignItem RootItem { public override DesignItem RootItem {
get { return _rootItem; } get { return _rootItem; }
} }
/// <summary>
/// Opens a new change group used to batch several changes.
/// ChangeGroups work as transactions and are used to support the Undo/Redo system.
/// </summary>
public override ChangeGroup OpenGroup(string changeGroupTitle, ICollection<DesignItem> affectedItems)
{
if (affectedItems == null)
throw new ArgumentNullException("affectedItems");
UndoService undoService = this.Services.GetService<UndoService>();
if (undoService == null)
throw new ServiceRequiredException(typeof(UndoService));
UndoTransaction g = undoService.StartTransaction(affectedItems);
g.Title = changeGroupTitle;
return g;
}
} }
} }

10
src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Xaml/XamlDesignItem.cs

@ -95,16 +95,6 @@ namespace ICSharpCode.WpfDesign.Designer.Xaml
} }
} }
public override ChangeGroup OpenGroup(string changeGroupTitle)
{
UndoService undoService = this.Services.GetService<UndoService>();
if (undoService == null)
throw new ServiceRequiredException(typeof(UndoService));
UndoTransaction g = undoService.StartTransaction(new DesignItem[] { this });
g.Title = changeGroupTitle;
return g;
}
internal void SetView(UIElement newView) internal void SetView(UIElement newView)
{ {
_view = newView; _view = newView;

116
src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Xaml/XamlModelCollectionElementsCollection.cs

@ -10,6 +10,7 @@ using System.Diagnostics;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using ICSharpCode.WpfDesign.XamlDom; using ICSharpCode.WpfDesign.XamlDom;
using ICSharpCode.WpfDesign.Designer.Services;
namespace ICSharpCode.WpfDesign.Designer.Xaml namespace ICSharpCode.WpfDesign.Designer.Xaml
{ {
@ -40,12 +41,14 @@ namespace ICSharpCode.WpfDesign.Designer.Xaml
public void Add(DesignItem item) public void Add(DesignItem item)
{ {
property.CollectionElements.Add(CheckItem(item).XamlObject); Insert(this.Count, item);
} }
public void Clear() public void Clear()
{ {
property.CollectionElements.Clear(); while (this.Count > 0) {
RemoveAt(this.Count - 1);
}
} }
public bool Contains(DesignItem item) public bool Contains(DesignItem item)
@ -68,16 +71,18 @@ namespace ICSharpCode.WpfDesign.Designer.Xaml
public void CopyTo(DesignItem[] array, int arrayIndex) public void CopyTo(DesignItem[] array, int arrayIndex)
{ {
Func.ToArray(this).CopyTo(array, arrayIndex); for (int i = 0; i < this.Count; i++) {
array[arrayIndex + i] = this[i];
}
} }
public bool Remove(DesignItem item) public bool Remove(DesignItem item)
{ {
XamlDesignItem xitem = CheckItemNoException(item); int index = IndexOf(item);
if (xitem != null) if (index < 0)
return property.CollectionElements.Remove(xitem.XamlObject);
else
return false; return false;
RemoveAt(index);
return true;
} }
public IEnumerator<DesignItem> GetEnumerator() public IEnumerator<DesignItem> GetEnumerator()
@ -122,18 +127,111 @@ namespace ICSharpCode.WpfDesign.Designer.Xaml
return GetItem(property.CollectionElements[index]); return GetItem(property.CollectionElements[index]);
} }
set { set {
property.CollectionElements[index] = CheckItem(value).XamlObject; RemoveAt(index);
Insert(index, value);
} }
} }
public void Insert(int index, DesignItem item) public void Insert(int index, DesignItem item)
{ {
property.CollectionElements.Insert(index, CheckItem(item).XamlObject); Execute(new InsertAction(this, index, CheckItem(item)));
} }
public void RemoveAt(int index) public void RemoveAt(int index)
{ {
Execute(new RemoveAtAction(this, index, (XamlDesignItem)this[index]));
}
void Execute(ITransactionItem item)
{
UndoService undoService = context.Services.GetService<UndoService>();
if (undoService != null)
undoService.Execute(item);
else
item.Do();
}
void RemoveInternal(int index, XamlDesignItem item)
{
Debug.Assert(property.CollectionElements[index] == item.XamlObject);
property.CollectionElements.RemoveAt(index); property.CollectionElements.RemoveAt(index);
} }
void InsertInternal(int index, XamlDesignItem item)
{
property.CollectionElements.Insert(index, item.XamlObject);
}
sealed class InsertAction : ITransactionItem
{
readonly XamlModelCollectionElementsCollection collection;
readonly int index;
readonly XamlDesignItem item;
public InsertAction(XamlModelCollectionElementsCollection collection, int index, XamlDesignItem item)
{
this.collection = collection;
this.index = index;
this.item = item;
}
public ICollection<DesignItem> AffectedElements {
get {
return new DesignItem[] { item };
}
}
public string Title {
get {
return "Insert into collection";
}
}
public void Do()
{
collection.InsertInternal(index, item);
}
public void Undo()
{
collection.RemoveInternal(index, item);
}
}
sealed class RemoveAtAction : ITransactionItem
{
readonly XamlModelCollectionElementsCollection collection;
readonly int index;
readonly XamlDesignItem item;
public RemoveAtAction(XamlModelCollectionElementsCollection collection, int index, XamlDesignItem item)
{
this.collection = collection;
this.index = index;
this.item = item;
}
public ICollection<DesignItem> AffectedElements {
get {
return new DesignItem[] { collection.modelProperty.DesignItem };
}
}
public string Title {
get {
return "Remove from collection";
}
}
public void Do()
{
collection.RemoveInternal(index, item);
}
public void Undo()
{
collection.InsertInternal(index, item);
}
}
} }
} }

7
src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/DesignContext.cs

@ -6,6 +6,7 @@
// </file> // </file>
using System; using System;
using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Xml; using System.Xml;
@ -45,5 +46,11 @@ namespace ICSharpCode.WpfDesign
/// Save the designed elements as XML. /// Save the designed elements as XML.
/// </summary> /// </summary>
public abstract void Save(XmlWriter writer); public abstract void Save(XmlWriter writer);
/// <summary>
/// Opens a new change group used to batch several changes.
/// ChangeGroups work as transactions and are used to support the Undo/Redo system.
/// </summary>
public abstract ChangeGroup OpenGroup(string changeGroupTitle, ICollection<DesignItem> affectedItems);
} }
} }

11
src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/DesignItem.cs

@ -72,10 +72,15 @@ namespace ICSharpCode.WpfDesign
} }
/// <summary> /// <summary>
/// Opens a new change group used to batch several changes. ChangeGroups work as transactions and /// Opens a new change group used to batch several changes.
/// are used to support the Undo/Redo system. /// ChangeGroups work as transactions and are used to support the Undo/Redo system.
/// Note: the ChangeGroup applies to the whole <see cref="DesignContext"/>, not just to
/// this item!
/// </summary> /// </summary>
public abstract ChangeGroup OpenGroup(string changeGroupTitle); public ChangeGroup OpenGroup(string changeGroupTitle)
{
return this.Context.OpenGroup(changeGroupTitle, new DesignItem[] { this });
}
#region Extensions support #region Extensions support
private struct ExtensionEntry private struct ExtensionEntry

38
src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Func.cs

@ -34,5 +34,43 @@ namespace ICSharpCode.WpfDesign
} }
} }
} }
/// <summary>
/// Returns the first element from <paramref name="input"/>.
/// </summary>
public static T First<T>(IEnumerable<T> input)
{
if (input == null)
throw new ArgumentNullException("input");
foreach (T item in input) {
return item;
}
throw new ArgumentException("input must not be an empty collection", "input");
}
/// <summary>
/// Skips the first <paramref name="skipCount"/> items in input and returns the rest.
/// </summary>
public static IEnumerable<T> Skip<T>(IEnumerable<T> input, long skipCount)
{
if (input == null)
throw new ArgumentNullException("input");
if (skipCount < 0)
throw new ArgumentOutOfRangeException("skipCount", skipCount, "skipCount must be non-negative.");
using (IEnumerator<T> enumerator = input.GetEnumerator()) {
if (skipCount != 0) {
long i = 0;
while (enumerator.MoveNext()) {
// skip item
if (++i == skipCount)
break;
}
if (i != skipCount) yield break; // MoveNext returned false, don't call it again
}
while (enumerator.MoveNext()) {
yield return enumerator.Current;
}
}
}
} }
} }

32
src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/PlacementBehavior.cs

@ -6,10 +6,8 @@
// </file> // </file>
using System; using System;
using System.Collections.Generic;
using System.Windows; using System.Windows;
using System.Windows.Input;
using ICSharpCode.WpfDesign.Adorners;
namespace ICSharpCode.WpfDesign namespace ICSharpCode.WpfDesign
{ {
@ -22,17 +20,27 @@ namespace ICSharpCode.WpfDesign
/// <summary> /// <summary>
/// Gets if the child element can be resized. /// Gets if the child element can be resized.
/// </summary> /// </summary>
bool CanPlace(DesignItem child, PlacementType type, PlacementAlignment position); bool CanPlace(ICollection<DesignItem> childItems, PlacementType type, PlacementAlignment position);
/// <summary>
/// Starts placement mode for this container.
/// </summary>
void BeginPlacement(PlacementOperation operation);
/// <summary>
/// Ends placement mode for this container.
/// </summary>
void EndPlacement(PlacementOperation operation);
/// <summary> /// <summary>
/// Starts placement mode of the child element specified in the placement operation. /// Gets the original position of the child item.
/// </summary> /// </summary>
void StartPlacement(PlacementOperation operation); Rect GetPosition(PlacementOperation operation, DesignItem child);
/// <summary> /// <summary>
/// Updates the placement of the element specified in the placement operation. /// Updates the placement of the element specified in the placement operation.
/// </summary> /// </summary>
void UpdatePlacement(PlacementOperation operation); void SetPosition(PlacementInformation info);
/// <summary> /// <summary>
/// Gets if leaving this container is allowed for the specified operation. /// Gets if leaving this container is allowed for the specified operation.
@ -40,7 +48,7 @@ namespace ICSharpCode.WpfDesign
bool CanLeaveContainer(PlacementOperation operation); bool CanLeaveContainer(PlacementOperation operation);
/// <summary> /// <summary>
/// Remove the placed child from this container. /// Remove the placed children from this container.
/// </summary> /// </summary>
void LeaveContainer(PlacementOperation operation); void LeaveContainer(PlacementOperation operation);
@ -50,15 +58,9 @@ namespace ICSharpCode.WpfDesign
bool CanEnterContainer(PlacementOperation operation); bool CanEnterContainer(PlacementOperation operation);
/// <summary> /// <summary>
/// Let the placed child enter this container. /// Let the placed children enter this container.
/// </summary> /// </summary>
void EnterContainer(PlacementOperation operation); void EnterContainer(PlacementOperation operation);
/// <summary>
/// Finishes placement of a child element. This method is called both for aborted
/// and committed placement operations.
/// </summary>
void FinishPlacement(PlacementOperation operation);
} }
/// <summary> /// <summary>

58
src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/PlacementInformation.cs

@ -0,0 +1,58 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Daniel Grunwald" email="daniel@danielgrunwald.de"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Windows;
namespace ICSharpCode.WpfDesign
{
/// <summary>
/// Stores information about the placement of an individual item during a <see cref="PlacementOperation"/>.
/// </summary>
public sealed class PlacementInformation
{
Rect originalBounds, bounds;
readonly DesignItem item;
readonly PlacementOperation operation;
internal PlacementInformation(DesignItem item, PlacementOperation operation)
{
this.item = item;
this.operation = operation;
}
/// <summary>
/// The item being placed.
/// </summary>
public DesignItem Item {
get { return item; }
}
/// <summary>
/// The <see cref="PlacementOperation"/> to which this PlacementInformation belongs.
/// </summary>
public PlacementOperation Operation {
get { return operation; }
}
/// <summary>
/// Gets/sets the original bounds.
/// </summary>
public Rect OriginalBounds {
get { return originalBounds; }
set { originalBounds = value; }
}
/// <summary>
/// Gets/sets the current bounds of the item.
/// </summary>
public Rect Bounds {
get { return bounds; }
set { bounds = value; }
}
}
}

90
src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/PlacementOperation.cs

@ -6,6 +6,8 @@
// </file> // </file>
using System; using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics; using System.Diagnostics;
using System.Windows; using System.Windows;
using System.Windows.Input; using System.Windows.Input;
@ -20,7 +22,7 @@ namespace ICSharpCode.WpfDesign
public sealed class PlacementOperation public sealed class PlacementOperation
{ {
readonly ChangeGroup changeGroup; readonly ChangeGroup changeGroup;
readonly DesignItem placedItem; readonly ReadOnlyCollection<PlacementInformation> placedItems;
readonly PlacementType type; readonly PlacementType type;
readonly DesignItem oldContainer; readonly DesignItem oldContainer;
readonly IPlacementBehavior oldContainerBehavior; readonly IPlacementBehavior oldContainerBehavior;
@ -30,10 +32,10 @@ namespace ICSharpCode.WpfDesign
#region Properties #region Properties
/// <summary> /// <summary>
/// The item being placed. /// The items being placed.
/// </summary> /// </summary>
public DesignItem PlacedItem { public ReadOnlyCollection<PlacementInformation> PlacedItems {
get { return placedItem; } get { return placedItems; }
} }
/// <summary> /// <summary>
@ -57,12 +59,6 @@ namespace ICSharpCode.WpfDesign
get { return isCommitted; } get { return isCommitted; }
} }
/// <summary>
/// The position of the left/right/top/bottom side in the coordinate system of the parent container.
/// These values must be set by IPlacementBehavior.StartPlacement and are updated by the drag operation.
/// </summary>
public double Left, Right, Top, Bottom;
/// <summary> /// <summary>
/// Gets the current container for the placement operation. /// Gets the current container for the placement operation.
/// </summary> /// </summary>
@ -79,6 +75,7 @@ namespace ICSharpCode.WpfDesign
#endregion #endregion
#region Container changing
/// <summary> /// <summary>
/// Make the placed item switch the container. /// Make the placed item switch the container.
/// This method assumes that you already have checked if changing the container is possible. /// This method assumes that you already have checked if changing the container is possible.
@ -93,6 +90,12 @@ namespace ICSharpCode.WpfDesign
try { try {
currentContainerBehavior.LeaveContainer(this); currentContainerBehavior.LeaveContainer(this);
System.Windows.Media.GeneralTransform transform = currentContainer.View.TransformToVisual(newContainer.View);
foreach (PlacementInformation info in placedItems) {
info.OriginalBounds = transform.TransformBounds(info.OriginalBounds);
info.Bounds = transform.TransformBounds(info.Bounds);
}
currentContainer = newContainer; currentContainer = newContainer;
currentContainerBehavior = newContainer.GetBehavior<IPlacementBehavior>(); currentContainerBehavior = newContainer.GetBehavior<IPlacementBehavior>();
@ -104,63 +107,82 @@ namespace ICSharpCode.WpfDesign
throw; throw;
} }
} }
#endregion
#region Start #region Start
/// <summary> /// <summary>
/// Starts a new placement operation that changes the placement of <paramref name="placedItem"/>. /// Starts a new placement operation that changes the placement of <paramref name="placedItem"/>.
/// </summary> /// </summary>
/// <param name="placedItem">The item to be placed.</param> /// <param name="placedItems">The items to be placed.</param>
/// <param name="type">The type of the placement.</param> /// <param name="type">The type of the placement.</param>
/// <returns>A PlacementOperation object.</returns> /// <returns>A PlacementOperation object.</returns>
/// <remarks> /// <remarks>
/// You MUST call either <see cref="Abort"/> or <see cref="Commit"/> on the returned PlacementOperation /// You MUST call either <see cref="Abort"/> or <see cref="Commit"/> on the returned PlacementOperation
/// once you are done with it, otherwise a ChangeGroup will be left open and Undo/Redo will fail to work! /// once you are done with it, otherwise a ChangeGroup will be left open and Undo/Redo will fail to work!
/// </remarks> /// </remarks>
public static PlacementOperation Start(DesignItem placedItem, PlacementType type) public static PlacementOperation Start(ICollection<DesignItem> placedItems, PlacementType type)
{ {
if (placedItem == null) if (placedItems == null)
throw new ArgumentNullException("placedItem"); throw new ArgumentNullException("placedItems");
if (type == null) if (type == null)
throw new ArgumentNullException("type"); throw new ArgumentNullException("type");
PlacementOperation op = new PlacementOperation(placedItem, type); DesignItem[] items = Func.ToArray(placedItems);
if (items.Length == 0)
throw new ArgumentException("placedItems.Length must be > 0");
PlacementOperation op = new PlacementOperation(items, type);
try { try {
if (op.currentContainerBehavior == null) if (op.currentContainerBehavior == null)
throw new InvalidOperationException("Starting the operation is not supported"); throw new InvalidOperationException("Starting the operation is not supported");
op.Left = op.Top = op.Bottom = op.Right = double.NaN;
op.currentContainerBehavior.StartPlacement(op); op.currentContainerBehavior.BeginPlacement(op);
if (double.IsNaN(op.Left) || double.IsNaN(op.Top) || double.IsNaN(op.Bottom) || double.IsNaN(op.Right)) foreach (PlacementInformation info in op.placedItems) {
throw new InvalidOperationException("IPlacementBehavior.StartPlacement must set Left,Top,Right+Bottom to non-NAN values"); info.OriginalBounds = op.currentContainerBehavior.GetPosition(op, info.Item);
info.Bounds = info.OriginalBounds;
}
} catch { } catch {
op.changeGroup.Abort(); op.changeGroup.Abort();
throw; throw;
} }
return op; return op;
} }
private PlacementOperation(DesignItem placedItem, PlacementType type) private PlacementOperation(DesignItem[] items, PlacementType type)
{ {
this.placedItem = placedItem; PlacementInformation[] information = new PlacementInformation[items.Length];
for (int i = 0; i < information.Length; i++) {
information[i] = new PlacementInformation(items[i], this);
}
this.placedItems = new ReadOnlyCollection<PlacementInformation>(information);
this.type = type; this.type = type;
this.oldContainer = placedItem.Parent; this.oldContainer = items[0].Parent;
this.oldContainerBehavior = GetPlacementBehavior(placedItem); this.oldContainerBehavior = GetPlacementBehavior(items);
this.currentContainer = oldContainer; this.currentContainer = oldContainer;
this.currentContainerBehavior = oldContainerBehavior; this.currentContainerBehavior = oldContainerBehavior;
this.changeGroup = placedItem.OpenGroup(type.ToString()); this.changeGroup = items[0].Context.OpenGroup(type.ToString(), items);
} }
/// <summary> /// <summary>
/// Gets the placement behavior associated with the specified item. /// Gets the placement behavior associated with the specified items.
/// </summary> /// </summary>
public static IPlacementBehavior GetPlacementBehavior(DesignItem item) public static IPlacementBehavior GetPlacementBehavior(ICollection<DesignItem> items)
{ {
if (item == null) if (items == null)
throw new ArgumentNullException("item"); throw new ArgumentNullException("items");
if (item.Parent != null) { if (items.Count == 0)
return item.Parent.GetBehavior<IPlacementBehavior>(); return null;
} else { DesignItem parent = Func.First(items).Parent;
return item.GetBehavior<IRootPlacementBehavior>(); foreach (DesignItem item in Func.Skip(items, 1)) {
if (item.Parent != parent)
return null;
} }
if (parent != null)
return parent.GetBehavior<IPlacementBehavior>();
else if (items.Count == 1)
return Func.First(items).GetBehavior<IRootPlacementBehavior>();
else
return null;
} }
#endregion #endregion
@ -183,7 +205,7 @@ namespace ICSharpCode.WpfDesign
if (isCommitted) if (isCommitted)
throw new InvalidOperationException("PlacementOperation is committed."); throw new InvalidOperationException("PlacementOperation is committed.");
isAborted = true; isAborted = true;
currentContainerBehavior.FinishPlacement(this); currentContainerBehavior.EndPlacement(this);
changeGroup.Abort(); changeGroup.Abort();
} }
} }
@ -197,7 +219,7 @@ namespace ICSharpCode.WpfDesign
if (isAborted || isCommitted) if (isAborted || isCommitted)
throw new InvalidOperationException("PlacementOperation is already aborted/committed."); throw new InvalidOperationException("PlacementOperation is already aborted/committed.");
isCommitted = true; isCommitted = true;
currentContainerBehavior.FinishPlacement(this); currentContainerBehavior.EndPlacement(this);
changeGroup.Commit(); changeGroup.Commit();
} }
#endregion #endregion

1
src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/WpfDesign.csproj

@ -63,6 +63,7 @@
<Compile Include="Adorners\AdornerProvider.cs" /> <Compile Include="Adorners\AdornerProvider.cs" />
<Compile Include="Adorners\AdornerProviderClasses.cs" /> <Compile Include="Adorners\AdornerProviderClasses.cs" />
<Compile Include="Adorners\RelativePlacement.cs" /> <Compile Include="Adorners\RelativePlacement.cs" />
<Compile Include="PlacementInformation.cs" />
<Compile Include="PlacementBehavior.cs" /> <Compile Include="PlacementBehavior.cs" />
<Compile Include="MouseInteraction.cs" /> <Compile Include="MouseInteraction.cs" />
<Compile Include="ChangeGroup.cs" /> <Compile Include="ChangeGroup.cs" />

Loading…
Cancel
Save