所以我试图在我的项目中实现一个移动和调整大小的动作。我看到了一些代码,并尝试使其正常工作,但无论我尝试每次点击都注册为Canvas
上的点击,即使我点击了ItemControl
的对象
我的XAML
看起来像这样:
<UserControl x:Class="Editor.Views.UI_Sketches.UiSketchCanvas"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:cc="clr-namespace:Editor.Views.CustomControls"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
mc:Ignorable="d"
Background="{DynamicResource BG}"
Height="Auto" Width="Auto">
<ItemsControl Name="UItemsControl" ItemsSource="{Binding FlattenedRectanglesCollection}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<cc:CustomCanvas Background="Transparent"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="Canvas.Left" Value="{Binding X}"></Setter>
<Setter Property="Canvas.Top" Value="{Binding Y}"></Setter>
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Label Content="{Binding Name}"/>
<Rectangle Width="{Binding Width}" Height="{Binding Height}" Stroke="White" Fill="DarkGray" IsHitTestVisible="False"></Rectangle>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
我的CustomCanvas
看起来像这样:
class CustomCanvas:Canvas
{
AdornerLayer aLayer;
bool _isDown;
bool _isDragging;
bool selected = false;
UIElement selectedElement = null;
private ContentPresenter selectedPresenter = null;
Point _startPoint;
private double _originalLeft;
private double _originalTop;
public CustomCanvas()
{
Loaded += CustomCanvas_OnLoaded;
}
private void CustomCanvas_OnLoaded(object sender, RoutedEventArgs e)
{
this.MouseLeftButtonDown += new MouseButtonEventHandler(CustomCanvas_MouseLeftButtonDown);
this.MouseLeftButtonUp += new MouseButtonEventHandler(DragFinishedMouseHandler);
this.MouseMove += new MouseEventHandler(CustomCanvas_MouseMove);
this.MouseLeave += new MouseEventHandler(CustomCanvas_MouseLeave);
PreviewMouseLeftButtonDown += new MouseButtonEventHandler(CustomCanvas_PreviewMouseLeftButtonDown);
PreviewMouseLeftButtonUp += new MouseButtonEventHandler(DragFinishedMouseHandler);
}
// Handler for drag stopping on leaving the window
void CustomCanvas_MouseLeave(object sender, MouseEventArgs e)
{
StopDragging();
e.Handled = true;
}
// Handler for drag stopping on user choise
void DragFinishedMouseHandler(object sender, MouseButtonEventArgs e)
{
StopDragging();
e.Handled = true;
}
// Method for stopping dragging
private void StopDragging()
{
if (_isDown)
{
_isDown = false;
_isDragging = false;
}
}
// Hanler for providing drag operation with selected element
void CustomCanvas_MouseMove(object sender, MouseEventArgs e)
{
if (_isDown)
{
if ((_isDragging == false) &&
((Math.Abs(e.GetPosition(this).X - _startPoint.X) > SystemParameters.MinimumHorizontalDragDistance) ||
(Math.Abs(e.GetPosition(this).Y - _startPoint.Y) > SystemParameters.MinimumVerticalDragDistance)))
_isDragging = true;
if (_isDragging)
{
Point position = Mouse.GetPosition(this);
Canvas.SetTop(selectedPresenter, position.Y - (_startPoint.Y - _originalTop));
Canvas.SetLeft(selectedPresenter, position.X - (_startPoint.X - _originalLeft));
}
}
}
// Handler for clearing element selection, adorner removal
void CustomCanvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (selected)
{
selected = false;
if (selectedElement != null)
{
aLayer.Remove(aLayer.GetAdorners(selectedElement)[0]);
selectedElement = null;
}
}
}
// Handler for element selection on the canvas providing resizing adorner
void CustomCanvas_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
selectedElement = e.Source as UIElement;
selectedPresenter = System.Windows.Media.VisualTreeHelper.GetParent(selectedElement) as ContentPresenter;
// Remove selection on clicking anywhere the window
if (selected)
{
selected = false;
if (selectedElement != null)
{
// Remove the adorner from the selected element
aLayer.Remove(aLayer.GetAdorners(selectedElement)[0]);
selectedElement = null;
}
}
// If any element except canvas is clicked,
// assign the selected element and add the adorner
if (e.Source != null)
{
_isDown = true;
_startPoint = e.GetPosition(this);
selectedElement = e.Source as UIElement;
_originalLeft = Canvas.GetLeft(selectedPresenter);
_originalTop = Canvas.GetTop(selectedPresenter);
aLayer = AdornerLayer.GetAdornerLayer(selectedElement);
aLayer.Add(new ResizingAdorner(selectedElement));
selected = true;
e.Handled = true;
}
}
}
ResizingAdorner看起来像这样:
public class ResizingAdorner : Adorner
{
// Resizing adorner uses Thumbs for visual elements.
// The Thumbs have built-in mouse input handling.
Thumb topLeft, topRight, bottomLeft, bottomRight;
// To store and manage the adorner's visual children.
VisualCollection visualChildren;
// Initialize the ResizingAdorner.
public ResizingAdorner(UIElement adornedElement)
: base(adornedElement)
{
visualChildren = new VisualCollection(this);
// Call a helper method to initialize the Thumbs
// with a customized cursors.
BuildAdornerCorner(ref topLeft, Cursors.SizeNWSE);
BuildAdornerCorner(ref topRight, Cursors.SizeNESW);
BuildAdornerCorner(ref bottomLeft, Cursors.SizeNESW);
BuildAdornerCorner(ref bottomRight, Cursors.SizeNWSE);
// Add handlers for resizing.
bottomLeft.DragDelta += new DragDeltaEventHandler(HandleBottomLeft);
bottomRight.DragDelta += new DragDeltaEventHandler(HandleBottomRight);
topLeft.DragDelta += new DragDeltaEventHandler(HandleTopLeft);
topRight.DragDelta += new DragDeltaEventHandler(HandleTopRight);
}
// Handler for resizing from the bottom-right.
void HandleBottomRight(object sender, DragDeltaEventArgs args)
{
FrameworkElement adornedElement = this.AdornedElement as FrameworkElement;
Thumb hitThumb = sender as Thumb;
if (adornedElement == null || hitThumb == null) return;
FrameworkElement parentElement = adornedElement.Parent as FrameworkElement;
// Ensure that the Width and Height are properly initialized after the resize.
EnforceSize(adornedElement);
// Change the size by the amount the user drags the mouse, as long as it's larger
// than the width or height of an adorner, respectively.
adornedElement.Width = Math.Max(adornedElement.Width + args.HorizontalChange, hitThumb.DesiredSize.Width);
adornedElement.Height = Math.Max(args.VerticalChange + adornedElement.Height, hitThumb.DesiredSize.Height);
}
// Handler for resizing from the top-right.
void HandleTopRight(object sender, DragDeltaEventArgs args)
{
FrameworkElement adornedElement = this.AdornedElement as FrameworkElement;
Thumb hitThumb = sender as Thumb;
if (adornedElement == null || hitThumb == null) return;
FrameworkElement parentElement = adornedElement.Parent as FrameworkElement;
// Ensure that the Width and Height are properly initialized after the resize.
EnforceSize(adornedElement);
// Change the size by the amount the user drags the mouse, as long as it's larger
// than the width or height of an adorner, respectively.
adornedElement.Width = Math.Max(adornedElement.Width + args.HorizontalChange, hitThumb.DesiredSize.Width);
//adornedElement.Height = Math.Max(adornedElement.Height - args.VerticalChange, hitThumb.DesiredSize.Height);
double height_old = adornedElement.Height;
double height_new = Math.Max(adornedElement.Height - args.VerticalChange, hitThumb.DesiredSize.Height);
double top_old = Canvas.GetTop(adornedElement);
adornedElement.Height = height_new;
Canvas.SetTop(adornedElement, top_old - (height_new - height_old));
}
// Handler for resizing from the top-left.
void HandleTopLeft(object sender, DragDeltaEventArgs args)
{
FrameworkElement adornedElement = AdornedElement as FrameworkElement;
Thumb hitThumb = sender as Thumb;
if (adornedElement == null || hitThumb == null) return;
// Ensure that the Width and Height are properly initialized after the resize.
EnforceSize(adornedElement);
// Change the size by the amount the user drags the mouse, as long as it's larger
// than the width or height of an adorner, respectively.
//adornedElement.Width = Math.Max(adornedElement.Width - args.HorizontalChange, hitThumb.DesiredSize.Width);
//adornedElement.Height = Math.Max(adornedElement.Height - args.VerticalChange, hitThumb.DesiredSize.Height);
double width_old = adornedElement.Width;
double width_new = Math.Max(adornedElement.Width - args.HorizontalChange, hitThumb.DesiredSize.Width);
double left_old = Canvas.GetLeft(adornedElement);
adornedElement.Width = width_new;
Canvas.SetLeft(adornedElement, left_old - (width_new - width_old));
double height_old = adornedElement.Height;
double height_new = Math.Max(adornedElement.Height - args.VerticalChange, hitThumb.DesiredSize.Height);
double top_old = Canvas.GetTop(adornedElement);
adornedElement.Height = height_new;
Canvas.SetTop(adornedElement, top_old - (height_new - height_old));
}
// Handler for resizing from the bottom-left.
void HandleBottomLeft(object sender, DragDeltaEventArgs args)
{
FrameworkElement adornedElement = AdornedElement as FrameworkElement;
Thumb hitThumb = sender as Thumb;
if (adornedElement == null || hitThumb == null) return;
// Ensure that the Width and Height are properly initialized after the resize.
EnforceSize(adornedElement);
// Change the size by the amount the user drags the mouse, as long as it's larger
// than the width or height of an adorner, respectively.
//adornedElement.Width = Math.Max(adornedElement.Width - args.HorizontalChange, hitThumb.DesiredSize.Width);
adornedElement.Height = Math.Max(args.VerticalChange + adornedElement.Height, hitThumb.DesiredSize.Height);
double width_old = adornedElement.Width;
double width_new = Math.Max(adornedElement.Width - args.HorizontalChange, hitThumb.DesiredSize.Width);
double left_old = Canvas.GetLeft(adornedElement);
adornedElement.Width = width_new;
Canvas.SetLeft(adornedElement, left_old - (width_new - width_old));
}
// Arrange the Adorners.
protected override Size ArrangeOverride(Size finalSize)
{
// desiredWidth and desiredHeight are the width and height of the element that's being adorned.
// These will be used to place the ResizingAdorner at the corners of the adorned element.
double desiredWidth = AdornedElement.DesiredSize.Width;
double desiredHeight = AdornedElement.DesiredSize.Height;
// adornerWidth & adornerHeight are used for placement as well.
double adornerWidth = this.DesiredSize.Width;
double adornerHeight = this.DesiredSize.Height;
topLeft.Arrange(new Rect(-adornerWidth / 2, -adornerHeight / 2, adornerWidth, adornerHeight));
topRight.Arrange(new Rect(desiredWidth - adornerWidth / 2, -adornerHeight / 2, adornerWidth, adornerHeight));
bottomLeft.Arrange(new Rect(-adornerWidth / 2, desiredHeight - adornerHeight / 2, adornerWidth, adornerHeight));
bottomRight.Arrange(new Rect(desiredWidth - adornerWidth / 2, desiredHeight - adornerHeight / 2, adornerWidth, adornerHeight));
// Return the final size.
return finalSize;
}
// Helper method to instantiate the corner Thumbs, set the Cursor property,
// set some appearance properties, and add the elements to the visual tree.
void BuildAdornerCorner(ref Thumb cornerThumb, Cursor customizedCursor)
{
if (cornerThumb != null) return;
cornerThumb = new Thumb();
// Set some arbitrary visual characteristics.
cornerThumb.Cursor = customizedCursor;
cornerThumb.Height = cornerThumb.Width = 10;
cornerThumb.Opacity = 0.40;
cornerThumb.Background = new SolidColorBrush(Colors.MediumBlue);
visualChildren.Add(cornerThumb);
}
// This method ensures that the Widths and Heights are initialized. Sizing to content produces
// Width and Height values of Double.NaN. Because this Adorner explicitly resizes, the Width and Height
// need to be set first. It also sets the maximum size of the adorned element.
void EnforceSize(FrameworkElement adornedElement)
{
if (adornedElement.Width.Equals(Double.NaN))
adornedElement.Width = adornedElement.DesiredSize.Width;
if (adornedElement.Height.Equals(Double.NaN))
adornedElement.Height = adornedElement.DesiredSize.Height;
FrameworkElement parent = adornedElement.Parent as FrameworkElement;
if (parent != null)
{
adornedElement.MaxHeight = parent.ActualHeight;
adornedElement.MaxWidth = parent.ActualWidth;
}
}
// Override the VisualChildrenCount and GetVisualChild properties to interface with
// the adorner's visual collection.
protected override int VisualChildrenCount { get { return visualChildren.Count; } }
protected override Visual GetVisualChild(int index) { return visualChildren[index]; }
}
你们可以告诉我为什么即使点击元素,我的源始终是CustomCanvas
吗?
我尝试在CustomCanvas_PreviewMouseLeftButtonDown
中获取点击的元素(矩形),然后获取父级(ContentPresenter
),以便我可以移动它。但无论我点击哪里,e.Source
始终是画布。
答案 0 :(得分:1)
我对自己的愚蠢感到惊讶,但是检查代码的每一行,我意识到IsHitTestVisible="False"
可能会导致在点击时检测到矩形的问题。