WPF Snap to object

时间:2018-01-16 20:45:37

标签: c# wpf

我正在开发一个实现流程图样式应用程序的项目。用节点和线连接它们。我有两个派生自Thumb的类(因为我想拖放它们。房间和退出。

每个房间都有一个List属性。我正在尝试将房间设置为每个出口都有一个ExitAdorner。退出装配工将连接到其他房间的其他出口装饰。我想要做的是,当我将ExitAdorner从一个房间拖到另一个房间时,我希望它能够捕捉到Room的边缘。在边缘,我希望它自由移动,以便用户可以选择他们想要ExitAdorner驻留的位置。有什么建议/方向看吗?

MainWindow.xaml

<Window x:Class="MUSHAround.MainWindow"
        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:local="clr-namespace:MUSHAround"
        xmlns:room="clr-namespace:MUSHAround.Models"
        mc:Ignorable="d"
        Title="MUSHAround" Height="350" Width="525" Background="White" MouseWheel="RoomCanvas_OnMouseWheel">
    <Window.Resources>
        <ResourceDictionary>

            <ControlTemplate x:Key="roomRect">
                <StackPanel>
                    <StackPanel.ContextMenu>
                        <ContextMenu>
                            <MenuItem Header="New Exit" Click="MenuItem_OnClick" CommandParameter="{Binding RelativeSource={RelativeSource Self}}"></MenuItem>
                        </ContextMenu>
                    </StackPanel.ContextMenu>
                    <Border BorderThickness="1" BorderBrush="Black" CornerRadius="10,10,10,10" Background="Beige" Width="120" Height="60" x:Name="border">
                        <Border.Effect>
                            <DropShadowEffect></DropShadowEffect>
                        </Border.Effect>
                        <StackPanel>
                            <TextBlock Text="{Binding Path=RoomName}" Margin="5,0,0,0"
                                       d:DataContext="{d:DesignInstance room:Room}"></TextBlock>
                            <TextBlock Text="{Binding Path=ParentRoom.Name}" Margin="5,0,0,0"></TextBlock>
                            <TextBlock Text="{Binding Path=ZoneMaster.Name}" Margin="5,0,0,0"></TextBlock>

                        </StackPanel>
                    </Border>
                </StackPanel>

            </ControlTemplate>
            <ControlTemplate x:Key="exitArrow">
                <Line Stroke="Black" StrokeThickness="2" x:Name="exitLine"></Line>
            </ControlTemplate>
        </ResourceDictionary>
    </Window.Resources>
    <Window.RenderTransform>
        <ScaleTransform x:Name="st">
        </ScaleTransform>
    </Window.RenderTransform>

    <DockPanel>
        <ToolBarTray DockPanel.Dock="Top">
            <ToolBar>
                <Button>
                    <StackPanel Orientation="Horizontal">
                        <TextBlock Text="New" Margin="0,0,5,0"/>
                        <Image Source="Assets/Images/AddScreen_16x_32.bmp"/>
                    </StackPanel>

                </Button>
            </ToolBar>
        </ToolBarTray>
    <Canvas Name="RoomCanvas" DockPanel.Dock="Bottom">
            <Canvas.ContextMenu>
                <ContextMenu>
                    <MenuItem Header="New Room" Click=" NewRoom_OnClick">
                    </MenuItem>
                </ContextMenu>
            </Canvas.ContextMenu>
        <Canvas.RenderTransform><ScaleTransform x:Name="canvasST"></ScaleTransform></Canvas.RenderTransform>
    </Canvas>
    </DockPanel>
</Window>

MainWindow.xaml.cs

using MUSHAround.Models;
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Documents;
using System.Windows.Input;

namespace MUSHAround
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private const double ScaleRate = 1.1;
        private Room firstRoom;
        private Room secondRoom;

        public MainWindow()
        {
            InitializeComponent();

            firstRoom = new Room();

            firstRoom.RoomName = "First Room";
            firstRoom.DataContext = firstRoom;
            firstRoom.Template = (ControlTemplate) this.FindResource("roomRect");
            firstRoom.AllowDrop = true;

            firstRoom.DragDelta += FirstRoom_OnDragDelta;
            RoomCanvas.Children.Add(firstRoom);
            Canvas.SetLeft(firstRoom, 0);
            Canvas.SetTop(firstRoom, 0);


            secondRoom = new Room();
            secondRoom.RoomName = "Second Room";
            secondRoom.DataContext = secondRoom;
            secondRoom.Template = (ControlTemplate) this.FindResource("roomRect");
            secondRoom.AllowDrop = true;

            secondRoom.DragDelta += FirstRoom_OnDragDelta;
            RoomCanvas.Children.Add(secondRoom);
            Canvas.SetTop(secondRoom, 100);
            Canvas.SetLeft(secondRoom, 200);


        }

        private void RoomCanvas_OnMouseWheel(object sender, MouseWheelEventArgs e)
        {
            if (e.Delta > 0)
            {
                canvasST.ScaleX *= ScaleRate;
                canvasST.ScaleY *= ScaleRate;
            }
            else
            {
                canvasST.ScaleX /= ScaleRate;
                canvasST.ScaleY /= ScaleRate;
            }
        }

        private void FirstRoom_OnDragDelta(object sender, DragDeltaEventArgs e)
        {
            if (e.Source is Room room)
            {
                Canvas.SetLeft(room, Canvas.GetLeft(room) + e.HorizontalChange);
                Canvas.SetTop(room, Canvas.GetTop(room) + e.VerticalChange);
            }
        }

        protected override void OnContentRendered(EventArgs e)
        {
            base.OnContentRendered(e);
            List<Room> rooms = new List<Room>();
            List<Exit> exits = new List<Exit>();

            foreach (UIElement child in RoomCanvas.Children)
            {
                if (child.GetType() == typeof(Room))
                {
                    rooms.Add(child as Room);
                }
            }

            foreach (Room room in rooms)
            {

            }
        }

        private void UpdateLines(Exit exit)
        {
            double left = Canvas.GetLeft(exit);
            double top = Canvas.GetTop(exit);

            for (int i = 0; i < exit.StartingLines.Count; i++)
            {
                exit.StartingLines[i].StartPoint = new Point(left + exit.ActualWidth / 2, top + exit.ActualHeight /2);
            }

            for (int i = 0; i < exit.EndingLines.Count; i++)
            {
                exit.EndingLines[i].EndPoint = new Point(left + exit.ActualWidth / 2, top + exit.ActualHeight /2);
            }
        }

        private void MenuItem_OnClick(object sender, RoutedEventArgs e)
        {
            MenuItem item = sender as MenuItem;

            StackPanel sp = ((ContextMenu) item.Parent).PlacementTarget as StackPanel;
            Room selectedRoom = sp.TemplatedParent as Room;
            var myAdornerLayer = AdornerLayer.GetAdornerLayer(selectedRoom);
            myAdornerLayer.Add(new ExitAdorner(selectedRoom));
        }

        private void NewRoom_OnClick(object sender, RoutedEventArgs e)
        {

        }

        public void AddRoom(Room newRoom)
        {

        }
    }
}

Room.cs

using System.Collections.Generic;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls.Primitives;
using System.Windows.Documents;
using System.Windows.Input;

namespace MUSHAround.Models
{
    public class Room : Thumb, INotifyPropertyChanged
    {
        private string _roomeName;
        private string _description;

        public string RoomName
        {
            get => _roomeName;

            set
            {
                if (value != _roomeName)
                {
                    _roomeName = value;
                    OnPropertyChanged(new PropertyChangedEventArgs("RoomName"));
                }
            }
        }

        public string Description
        {
            get => _description;
            set
            {
                if (value != _description)
                {
                    _description = value;
                    OnPropertyChanged(new PropertyChangedEventArgs("Description"));
                }
            }
        }
        public Dictionary<string, string> Attributes { get; set; }
        public ParentRoom ParentRoom { get; set; }
        public ZoneMaster ZoneMaster { get; set; }
        public List<Exit> Exits { get; set; }
        public List<string> Flags { get; set; }
        public string Conformat { get; set; }
        public string Descformat { get; set; }
        public string Exitformat { get; set; }
        public string Nameformat { get; set; }

        public event PropertyChangedEventHandler PropertyChanged;

        public Room()
        {
            Attributes = new Dictionary<string, string>();
            Flags = new List<string>();
            Exits = new List<Exit>();
        }

        private void OnPropertyChanged(PropertyChangedEventArgs e)
        {
            PropertyChanged?.Invoke(this, e);
        }

        protected override void OnDrop(DragEventArgs e)
        {
            base.OnDrop(e);
            if (e.Source is Exit)
            {
                System.Windows.Forms.MessageBox.Show("Dropped an exit!");
            }

        }

        protected override void OnMouseEnter(MouseEventArgs e)
        {
            base.OnMouseEnter(e);
            Room source = e.Source as Room;
            var adornerLayer = AdornerLayer.GetAdornerLayer(this);
            adornerLayer.Add(new ResizeAdorner(source));
        }

        public void Redraw()
        {
            InvalidateVisual();
        }
    }
}

Exit.cs

using System.Collections.Generic;
using System.Windows.Controls.Primitives;
using System.Windows.Media;

namespace MUSHAround.Models
{
    public class Exit : Thumb
    {
        public List<LineGeometry> StartingLines { get; set; }
        public List<LineGeometry> EndingLines { get; set; }

        public string ExitName { get; set; }
        public string ReturnName { get; set; }
        public Room Location { get; set; }
        public Room Destination { get; set; }
        public string DropMessage { get; set; }
        public string SuccessMessage { get; set; }
        public string OSuccessMessage { get; set; }

        public Exit()
        {
            StartingLines = new List<LineGeometry>();
            EndingLines = new List<LineGeometry>();
        }
    }
}

ExitAdorner.cs

using System.Windows;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;

namespace MUSHAround.Models
{
    public class ExitAdorner : Adorner
    {
        public Exit Exit { get; set; }
        private SolidColorBrush renderBrush;

        public ExitAdorner(UIElement adornedElement) : base(adornedElement)
        {
            renderBrush = new SolidColorBrush(Colors.White);
            Exit = new Exit();
            Room owner = adornedElement as Room;
            owner.Exits.Add(Exit);
        }

        protected override void OnRender(DrawingContext drawingContext)
        {
            //base.OnRender(drawingContext);
            Rect adornedRect = new Rect(this.AdornedElement.DesiredSize);

            //SolidColorBrush renderBrush = new SolidColorBrush(Colors.Beige);
            renderBrush.Opacity = 1;
            Pen renderPen = new Pen(new SolidColorBrush(Colors.Black), 1);
            double renderRadius = 4.0;

            drawingContext.DrawEllipse(renderBrush, renderPen, new Point(adornedRect.Top + (adornedRect.Width/2),adornedRect.Top), renderRadius, renderRadius);
        }

        protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
        {
            //base.OnMouseLeftButtonDown(e);
            MessageBox.Show("Clicked!");
        }

        protected override void OnMouseEnter(MouseEventArgs e)
        {
            base.OnMouseEnter(e);
            renderBrush = new SolidColorBrush(Colors.Black);
            InvalidateVisual();
        }

        protected override void OnMouseLeave(MouseEventArgs e)
        {
            base.OnMouseLeave(e);
            renderBrush = new SolidColorBrush(Colors.White);
            InvalidateVisual();
        }


    }
}

0 个答案:

没有答案