我正在开发一个实现流程图样式应用程序的项目。用节点和线连接它们。我有两个派生自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();
}
}
}