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

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

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

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

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

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

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

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

@ -6,6 +6,7 @@ @@ -6,6 +6,7 @@
// </file>
using System;
using System.Collections.Generic;
using System.Xml;
using ICSharpCode.WpfDesign.XamlDom;
using ICSharpCode.WpfDesign.Designer.Services;
@ -84,5 +85,22 @@ namespace ICSharpCode.WpfDesign.Designer.Xaml @@ -84,5 +85,22 @@ namespace ICSharpCode.WpfDesign.Designer.Xaml
public override DesignItem 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 @@ -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)
{
_view = newView;

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

@ -10,6 +10,7 @@ using System.Diagnostics; @@ -10,6 +10,7 @@ using System.Diagnostics;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using ICSharpCode.WpfDesign.XamlDom;
using ICSharpCode.WpfDesign.Designer.Services;
namespace ICSharpCode.WpfDesign.Designer.Xaml
{
@ -40,12 +41,14 @@ namespace ICSharpCode.WpfDesign.Designer.Xaml @@ -40,12 +41,14 @@ namespace ICSharpCode.WpfDesign.Designer.Xaml
public void Add(DesignItem item)
{
property.CollectionElements.Add(CheckItem(item).XamlObject);
Insert(this.Count, item);
}
public void Clear()
{
property.CollectionElements.Clear();
while (this.Count > 0) {
RemoveAt(this.Count - 1);
}
}
public bool Contains(DesignItem item)
@ -68,16 +71,18 @@ namespace ICSharpCode.WpfDesign.Designer.Xaml @@ -68,16 +71,18 @@ namespace ICSharpCode.WpfDesign.Designer.Xaml
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)
{
XamlDesignItem xitem = CheckItemNoException(item);
if (xitem != null)
return property.CollectionElements.Remove(xitem.XamlObject);
else
int index = IndexOf(item);
if (index < 0)
return false;
RemoveAt(index);
return true;
}
public IEnumerator<DesignItem> GetEnumerator()
@ -122,18 +127,111 @@ namespace ICSharpCode.WpfDesign.Designer.Xaml @@ -122,18 +127,111 @@ namespace ICSharpCode.WpfDesign.Designer.Xaml
return GetItem(property.CollectionElements[index]);
}
set {
property.CollectionElements[index] = CheckItem(value).XamlObject;
RemoveAt(index);
Insert(index, value);
}
}
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)
{
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);
}
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 @@ @@ -6,6 +6,7 @@
// </file>
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Xml;
@ -45,5 +46,11 @@ namespace ICSharpCode.WpfDesign @@ -45,5 +46,11 @@ namespace ICSharpCode.WpfDesign
/// Save the designed elements as XML.
/// </summary>
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 @@ -72,10 +72,15 @@ namespace ICSharpCode.WpfDesign
}
/// <summary>
/// Opens a new change group used to batch several changes. ChangeGroups work as transactions and
/// are used to support the Undo/Redo system.
/// Opens a new change group used to batch several changes.
/// 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>
public abstract ChangeGroup OpenGroup(string changeGroupTitle);
public ChangeGroup OpenGroup(string changeGroupTitle)
{
return this.Context.OpenGroup(changeGroupTitle, new DesignItem[] { this });
}
#region Extensions support
private struct ExtensionEntry

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

@ -34,5 +34,43 @@ namespace ICSharpCode.WpfDesign @@ -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 @@ @@ -6,10 +6,8 @@
// </file>
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Input;
using ICSharpCode.WpfDesign.Adorners;
namespace ICSharpCode.WpfDesign
{
@ -22,17 +20,27 @@ namespace ICSharpCode.WpfDesign @@ -22,17 +20,27 @@ namespace ICSharpCode.WpfDesign
/// <summary>
/// Gets if the child element can be resized.
/// </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>
/// Starts placement mode of the child element specified in the placement operation.
/// Gets the original position of the child item.
/// </summary>
void StartPlacement(PlacementOperation operation);
Rect GetPosition(PlacementOperation operation, DesignItem child);
/// <summary>
/// Updates the placement of the element specified in the placement operation.
/// </summary>
void UpdatePlacement(PlacementOperation operation);
void SetPosition(PlacementInformation info);
/// <summary>
/// Gets if leaving this container is allowed for the specified operation.
@ -40,7 +48,7 @@ namespace ICSharpCode.WpfDesign @@ -40,7 +48,7 @@ namespace ICSharpCode.WpfDesign
bool CanLeaveContainer(PlacementOperation operation);
/// <summary>
/// Remove the placed child from this container.
/// Remove the placed children from this container.
/// </summary>
void LeaveContainer(PlacementOperation operation);
@ -50,15 +58,9 @@ namespace ICSharpCode.WpfDesign @@ -50,15 +58,9 @@ namespace ICSharpCode.WpfDesign
bool CanEnterContainer(PlacementOperation operation);
/// <summary>
/// Let the placed child enter this container.
/// Let the placed children enter this container.
/// </summary>
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>

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

@ -0,0 +1,58 @@ @@ -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 @@ @@ -6,6 +6,8 @@
// </file>
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Windows;
using System.Windows.Input;
@ -20,7 +22,7 @@ namespace ICSharpCode.WpfDesign @@ -20,7 +22,7 @@ namespace ICSharpCode.WpfDesign
public sealed class PlacementOperation
{
readonly ChangeGroup changeGroup;
readonly DesignItem placedItem;
readonly ReadOnlyCollection<PlacementInformation> placedItems;
readonly PlacementType type;
readonly DesignItem oldContainer;
readonly IPlacementBehavior oldContainerBehavior;
@ -30,10 +32,10 @@ namespace ICSharpCode.WpfDesign @@ -30,10 +32,10 @@ namespace ICSharpCode.WpfDesign
#region Properties
/// <summary>
/// The item being placed.
/// The items being placed.
/// </summary>
public DesignItem PlacedItem {
get { return placedItem; }
public ReadOnlyCollection<PlacementInformation> PlacedItems {
get { return placedItems; }
}
/// <summary>
@ -57,12 +59,6 @@ namespace ICSharpCode.WpfDesign @@ -57,12 +59,6 @@ namespace ICSharpCode.WpfDesign
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>
/// Gets the current container for the placement operation.
/// </summary>
@ -79,6 +75,7 @@ namespace ICSharpCode.WpfDesign @@ -79,6 +75,7 @@ namespace ICSharpCode.WpfDesign
#endregion
#region Container changing
/// <summary>
/// Make the placed item switch the container.
/// This method assumes that you already have checked if changing the container is possible.
@ -93,6 +90,12 @@ namespace ICSharpCode.WpfDesign @@ -93,6 +90,12 @@ namespace ICSharpCode.WpfDesign
try {
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;
currentContainerBehavior = newContainer.GetBehavior<IPlacementBehavior>();
@ -104,63 +107,82 @@ namespace ICSharpCode.WpfDesign @@ -104,63 +107,82 @@ namespace ICSharpCode.WpfDesign
throw;
}
}
#endregion
#region Start
/// <summary>
/// Starts a new placement operation that changes the placement of <paramref name="placedItem"/>.
/// </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>
/// <returns>A PlacementOperation object.</returns>
/// <remarks>
/// 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!
/// </remarks>
public static PlacementOperation Start(DesignItem placedItem, PlacementType type)
public static PlacementOperation Start(ICollection<DesignItem> placedItems, PlacementType type)
{
if (placedItem == null)
throw new ArgumentNullException("placedItem");
if (placedItems == null)
throw new ArgumentNullException("placedItems");
if (type == null)
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 {
if (op.currentContainerBehavior == null)
throw new InvalidOperationException("Starting the operation is not supported");
op.Left = op.Top = op.Bottom = op.Right = double.NaN;
op.currentContainerBehavior.StartPlacement(op);
if (double.IsNaN(op.Left) || double.IsNaN(op.Top) || double.IsNaN(op.Bottom) || double.IsNaN(op.Right))
throw new InvalidOperationException("IPlacementBehavior.StartPlacement must set Left,Top,Right+Bottom to non-NAN values");
op.currentContainerBehavior.BeginPlacement(op);
foreach (PlacementInformation info in op.placedItems) {
info.OriginalBounds = op.currentContainerBehavior.GetPosition(op, info.Item);
info.Bounds = info.OriginalBounds;
}
} catch {
op.changeGroup.Abort();
throw;
}
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.oldContainer = placedItem.Parent;
this.oldContainerBehavior = GetPlacementBehavior(placedItem);
this.oldContainer = items[0].Parent;
this.oldContainerBehavior = GetPlacementBehavior(items);
this.currentContainer = oldContainer;
this.currentContainerBehavior = oldContainerBehavior;
this.changeGroup = placedItem.OpenGroup(type.ToString());
this.changeGroup = items[0].Context.OpenGroup(type.ToString(), items);
}
/// <summary>
/// Gets the placement behavior associated with the specified item.
/// Gets the placement behavior associated with the specified items.
/// </summary>
public static IPlacementBehavior GetPlacementBehavior(DesignItem item)
public static IPlacementBehavior GetPlacementBehavior(ICollection<DesignItem> items)
{
if (item == null)
throw new ArgumentNullException("item");
if (item.Parent != null) {
return item.Parent.GetBehavior<IPlacementBehavior>();
} else {
return item.GetBehavior<IRootPlacementBehavior>();
if (items == null)
throw new ArgumentNullException("items");
if (items.Count == 0)
return null;
DesignItem parent = Func.First(items).Parent;
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
@ -183,7 +205,7 @@ namespace ICSharpCode.WpfDesign @@ -183,7 +205,7 @@ namespace ICSharpCode.WpfDesign
if (isCommitted)
throw new InvalidOperationException("PlacementOperation is committed.");
isAborted = true;
currentContainerBehavior.FinishPlacement(this);
currentContainerBehavior.EndPlacement(this);
changeGroup.Abort();
}
}
@ -197,7 +219,7 @@ namespace ICSharpCode.WpfDesign @@ -197,7 +219,7 @@ namespace ICSharpCode.WpfDesign
if (isAborted || isCommitted)
throw new InvalidOperationException("PlacementOperation is already aborted/committed.");
isCommitted = true;
currentContainerBehavior.FinishPlacement(this);
currentContainerBehavior.EndPlacement(this);
changeGroup.Commit();
}
#endregion

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

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

Loading…
Cancel
Save