1

那里有许多用于 WPF 的 Resizing Adorner 示例,但我还没有找到一个可以很好地与简单 Line 一起使用的示例……显然我希望控制点显示在行的末端,而不是围绕它形成一个矩形。有人遇到过这样的事情吗?

4

1 回答 1

3

我的 Line Adorner 实现,您可以移动起点和终点或移动线(当前已评论)。

public class LineAdorner : Adorner
{
    bool IsStartPoint = false;
    bool IsControlModeOn = false;
    Size size = new Size(10, 10);
    SnapToGrid snap = new SnapToGrid();

    public LineAdorner(UIElement adornedElement)
        : base(adornedElement)
    {
        this.MouseLeftButtonDown += new MouseButtonEventHandler(LineAdorner_MouseLeftButtonDown);
        this.MouseLeftButtonUp += new MouseButtonEventHandler(LineAdorner_MouseLeftButtonUp);
        this.MouseMove += new MouseEventHandler(LineAdorner_MouseMove);
    }

    void LineAdorner_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        if (Keyboard.Modifiers == ModifierKeys.Control)
            IsControlModeOn = true;

        Line line = this.AdornedElement as Line;
        LineItem lineItem = line.DataContext as LineItem;
        Point p = snap.Snap(e.GetPosition(line), SnapToGrid.SnapMode.Move);

        double dStart = 0.0;
        double dEnd = 0.0;

        if (!this.IsMouseCaptured)
        {
            dStart = Math.Sqrt(Math.Pow(lineItem.StartPoint.X - p.X, 2) + Math.Pow(lineItem.StartPoint.Y - p.Y, 2));
            dEnd = Math.Sqrt(Math.Pow(lineItem.EndPoint.X - p.X, 2) + Math.Pow(lineItem.EndPoint.Y - p.Y, 2));
        }

        if (IsControlModeOn)
        {
            if (this.IsMouseCaptured)
            {
                if (IsStartPoint)
                    lineItem.StartPoint = p;
                else
                    lineItem.EndPoint = p;

                this.InvalidateVisual();
                this.ReleaseMouseCapture();

                IsControlModeOn = false;
            }
            else
            {
                if (dStart < dEnd)
                    IsStartPoint = true;
                else
                    IsStartPoint = false;

                this.InvalidateVisual();
                this.CaptureMouse();
            }
        }
        else
        {
            if (!this.IsMouseCaptured)
            {
                if (dStart < dEnd)
                    IsStartPoint = true;
                else
                    IsStartPoint = false;

                this.InvalidateVisual();
                this.CaptureMouse();
            }
        }
    }

    void LineAdorner_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
    {
        if (!IsControlModeOn)
        {
            if (this.IsMouseCaptured)
            {
                Line line = this.AdornedElement as Line;
                LineItem lineItem = line.DataContext as LineItem;
                Point p = snap.Snap(e.GetPosition(line), SnapToGrid.SnapMode.Move);

                if (IsStartPoint)
                    lineItem.StartPoint = p;
                else
                    lineItem.EndPoint = p;

                this.InvalidateVisual();
                this.ReleaseMouseCapture();
            }
        }
    }

    void LineAdorner_MouseMove(object sender, MouseEventArgs e)
    {
        if (this.IsMouseCaptured)
        {
            if (this.AdornedElement.GetType() == typeof(Line))
            {
                Line line = this.AdornedElement as Line;
                LineItem lineItem = line.DataContext as LineItem;
                Point p = snap.Snap(e.GetPosition(line), SnapToGrid.SnapMode.Move);

                // mode: move start or end point

                //*
                if (IsStartPoint)
                    lineItem.StartPoint = p;
                else
                    lineItem.EndPoint = p;
                //*/

                // mode: move line

                /*
                if (IsStartPoint)
                {
                    double dX = lineItem.StartPoint.X - p.X;
                    double dY = lineItem.StartPoint.Y - p.Y;
                    Point pEnd = new Point(lineItem.EndPoint.X - dX, lineItem.EndPoint.Y - dY);

                    lineItem.StartPoint = p;
                    lineItem.EndPoint = pEnd;
                }
                else
                {
                    double dX = lineItem.EndPoint.X - p.X;
                    double dY = lineItem.EndPoint.Y - p.Y;
                    Point pStart = new Point(lineItem.StartPoint.X - dX, lineItem.StartPoint.Y - dY);

                    lineItem.EndPoint = p;
                    lineItem.StartPoint = pStart;
                }
                */

                this.InvalidateVisual();
            }
        }
    }

    protected override void OnRender(DrawingContext drawingContext)
    {
        if (this.AdornedElement.GetType() == typeof(Line))
        {
            Line line = this.AdornedElement as Line;
            LineItem lineItem = line.DataContext as LineItem;

            SolidColorBrush brush = new SolidColorBrush(Color.FromArgb(255, 255, 255, 255));
            SolidColorBrush penBrush = new SolidColorBrush(Color.FromArgb(255, 0, 0, 0));
            Pen pen = new Pen(penBrush, 1.0);

            Point p1 = new Point(lineItem.StartPoint.X, lineItem.StartPoint.Y);
            Point p2 = new Point(lineItem.EndPoint.X, lineItem.EndPoint.Y);

            p1.Offset(-size.Width / 2, -size.Height / 2);
            p2.Offset(-size.Width / 2, -size.Height / 2);

            Rect r1 = new Rect(p1, size);
            Rect r2 = new Rect(p2, size);

            double halfPenWidth = pen.Thickness / 2;

            GuidelineSet g1 = new GuidelineSet();
            g1.GuidelinesX.Add(r1.Left + halfPenWidth);
            g1.GuidelinesX.Add(r1.Right + halfPenWidth);
            g1.GuidelinesY.Add(r1.Top + halfPenWidth);
            g1.GuidelinesY.Add(r1.Bottom + halfPenWidth);
            drawingContext.PushGuidelineSet(g1);

            drawingContext.DrawRectangle(brush, pen, r1);
            drawingContext.Pop();

            GuidelineSet g2 = new GuidelineSet();
            g2.GuidelinesX.Add(r2.Left + halfPenWidth);
            g2.GuidelinesX.Add(r2.Right + halfPenWidth);
            g2.GuidelinesY.Add(r2.Top + halfPenWidth);
            g2.GuidelinesY.Add(r2.Bottom + halfPenWidth);
            drawingContext.PushGuidelineSet(g2);

            drawingContext.DrawRectangle(brush, pen, r2);
            drawingContext.Pop();
        }

        base.OnRender(drawingContext);
    }
}

这是用于捕捉点的 SnapToGrid 类(如果不需要,可以从代码中删除)。

class SnapToGrid
{
    private Size gridSizeModeCreate = new Size(13.5, 13.5);
    private Size gridSizeModeMove = new Size(13.5, 13.5);
    private double gridOffsetX = -1.0;
    private double gridOffsetY = -1.0;

    public enum SnapMode
    {
        Create,
        Move,
        Line
    }

    public Size GridSizeModeCreate
    {
        get { return gridSizeModeCreate; }
        set
        {
            gridSizeModeCreate = value;
        }
    }

    public Size GridSizeModeMove
    {
        get { return gridSizeModeMove; }
        set
        {
            gridSizeModeMove = value;
        }
    }

    public double GridOffsetX
    {
        get { return gridOffsetX; }
        set
        {
            gridOffsetX = value;
        }
    }

    public double GridOffsetY
    {
        get { return gridOffsetY; }
        set
        {
            gridOffsetY = value;
        }
    }

    private Point Calculate(Point p, Size s)
    {
        double snapX = p.X + ((Math.Round(p.X / s.Width) - p.X / s.Width) * s.Width);
        double snapY = p.Y + ((Math.Round(p.Y / s.Height) - p.Y / s.Height) * s.Height);

        return new Point(snapX + gridOffsetX, snapY + gridOffsetY);
    }

    public Point Snap(Point p, SnapMode mode)
    {
        if (mode == SnapMode.Create)
            return Calculate(p, gridSizeModeCreate);
        else if (mode == SnapMode.Move)
            return Calculate(p, gridSizeModeMove);
        else
            return new Point(0, 0);
    }
}

以下是模型对象:

public class LineItem : NotifyObject
{
    private Point startPoint;
    private Point endPoint;

    public Point StartPoint
    {
        get
        {
            return startPoint;
        }
        set
        {
            if (startPoint != value)
            {
                startPoint = value;
                OnPropertyChanged("StartPoint");
            }
        }
    }

    public Point EndPoint
    {
        get
        {
            return endPoint;
        }
        set
        {
            if (endPoint != value)
            {
                endPoint = value;
                OnPropertyChanged("EndPoint");
            }
        }
    }
}

public class NotifyObject : INotifyPropertyChanged
{
    protected virtual void OnPropertyChanged(string propertyChanged)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyChanged));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
}
于 2011-04-21T18:56:07.277 回答