mirror of
https://github.com/egmont11/NetworkDiagram.git
synced 2026-07-24 07:09:43 +02:00
"Infinite" workspace for diagrams
This commit is contained in:
@@ -73,12 +73,23 @@
|
|||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
<!-- Canvas Area -->
|
<!-- Canvas Area -->
|
||||||
<ScrollViewer Grid.Row="1" Grid.Column="1" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" Background="Transparent">
|
<Grid Grid.Row="1" Grid.Column="1" x:Name="CanvasViewport" ClipToBounds="True" Background="Transparent"
|
||||||
<Canvas x:Name="DiagramCanvas" Width="2000" Height="2000" AllowDrop="True"
|
MouseDown="Viewport_MouseDown"
|
||||||
|
MouseMove="Viewport_MouseMove"
|
||||||
|
MouseUp="Viewport_MouseUp"
|
||||||
|
MouseWheel="Viewport_MouseWheel">
|
||||||
|
<!-- Increased size to 40k to provide plenty of space in all directions -->
|
||||||
|
<Canvas x:Name="DiagramCanvas" Width="40000" Height="40000" AllowDrop="True"
|
||||||
Drop="DiagramCanvas_Drop" DragOver="DiagramCanvas_DragOver"
|
Drop="DiagramCanvas_Drop" DragOver="DiagramCanvas_DragOver"
|
||||||
MouseDown="DiagramCanvas_MouseDown"
|
MouseDown="DiagramCanvas_MouseDown"
|
||||||
MouseMove="DiagramCanvas_MouseMove"
|
MouseMove="DiagramCanvas_MouseMove"
|
||||||
MouseUp="DiagramCanvas_MouseUp">
|
MouseUp="DiagramCanvas_MouseUp">
|
||||||
|
<Canvas.RenderTransform>
|
||||||
|
<TransformGroup>
|
||||||
|
<ScaleTransform x:Name="CanvasScale" ScaleX="1" ScaleY="1"/>
|
||||||
|
<TranslateTransform x:Name="CanvasTranslate" X="-20000" Y="-20000"/>
|
||||||
|
</TransformGroup>
|
||||||
|
</Canvas.RenderTransform>
|
||||||
<Canvas.Background>
|
<Canvas.Background>
|
||||||
<VisualBrush TileMode="Tile" Viewport="0,0,40,40" ViewportUnits="Absolute">
|
<VisualBrush TileMode="Tile" Viewport="0,0,40,40" ViewportUnits="Absolute">
|
||||||
<VisualBrush.Visual>
|
<VisualBrush.Visual>
|
||||||
@@ -122,6 +133,6 @@
|
|||||||
<Rectangle x:Name="SelectionRect" Stroke="{StaticResource AccentBlue}" StrokeThickness="1.5" StrokeDashArray="3,3"
|
<Rectangle x:Name="SelectionRect" Stroke="{StaticResource AccentBlue}" StrokeThickness="1.5" StrokeDashArray="3,3"
|
||||||
Fill="#220078D7" Visibility="Collapsed" Panel.ZIndex="9999" RadiusX="2" RadiusY="2" IsHitTestVisible="False"/>
|
Fill="#220078D7" Visibility="Collapsed" Panel.ZIndex="9999" RadiusX="2" RadiusY="2" IsHitTestVisible="False"/>
|
||||||
</Canvas>
|
</Canvas>
|
||||||
</ScrollViewer>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Window>
|
</Window>
|
||||||
|
|||||||
@@ -30,12 +30,85 @@ namespace NetworkDiagram
|
|||||||
private readonly Dictionary<Connection, Line> _connectionLines = new();
|
private readonly Dictionary<Connection, Line> _connectionLines = new();
|
||||||
private readonly Dictionary<PlacedDevice, FrameworkElement> _deviceElements = new();
|
private readonly Dictionary<PlacedDevice, FrameworkElement> _deviceElements = new();
|
||||||
|
|
||||||
|
// Panning and Zooming
|
||||||
|
private Point _lastPanPoint;
|
||||||
|
private bool _isPanning;
|
||||||
|
|
||||||
public MainWindow()
|
public MainWindow()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
LoadTemplates();
|
LoadTemplates();
|
||||||
|
this.Loaded += MainWindow_Loaded;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
CenterView();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CenterView()
|
||||||
|
{
|
||||||
|
// Center logically on (20000, 20000) minus half the viewport size to put 0,0 in the middle
|
||||||
|
CanvasTranslate.X = -20000 + (CanvasViewport.ActualWidth / 2);
|
||||||
|
CanvasTranslate.Y = -20000 + (CanvasViewport.ActualHeight / 2);
|
||||||
|
CanvasScale.ScaleX = 1.0;
|
||||||
|
CanvasScale.ScaleY = 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Panning and Zooming
|
||||||
|
private void Viewport_MouseDown(object sender, MouseButtonEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.ChangedButton == MouseButton.Middle)
|
||||||
|
{
|
||||||
|
_isPanning = true;
|
||||||
|
_lastPanPoint = e.GetPosition(CanvasViewport);
|
||||||
|
CanvasViewport.CaptureMouse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Viewport_MouseMove(object sender, MouseEventArgs e)
|
||||||
|
{
|
||||||
|
if (_isPanning)
|
||||||
|
{
|
||||||
|
Point currentPoint = e.GetPosition(CanvasViewport);
|
||||||
|
double deltaX = currentPoint.X - _lastPanPoint.X;
|
||||||
|
double deltaY = currentPoint.Y - _lastPanPoint.Y;
|
||||||
|
|
||||||
|
CanvasTranslate.X += deltaX;
|
||||||
|
CanvasTranslate.Y += deltaY;
|
||||||
|
|
||||||
|
_lastPanPoint = currentPoint;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Viewport_MouseUp(object sender, MouseButtonEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.ChangedButton == MouseButton.Middle)
|
||||||
|
{
|
||||||
|
_isPanning = false;
|
||||||
|
CanvasViewport.ReleaseMouseCapture();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Viewport_MouseWheel(object sender, MouseWheelEventArgs e)
|
||||||
|
{
|
||||||
|
double zoom = e.Delta > 0 ? 1.1 : 0.9;
|
||||||
|
|
||||||
|
double newScale = CanvasScale.ScaleX * zoom;
|
||||||
|
if (newScale < 0.1 || newScale > 5) return;
|
||||||
|
|
||||||
|
Point mousePos = e.GetPosition(DiagramCanvas);
|
||||||
|
|
||||||
|
// Zoom centered on mouse
|
||||||
|
CanvasScale.ScaleX = newScale;
|
||||||
|
CanvasScale.ScaleY = newScale;
|
||||||
|
|
||||||
|
Point newMousePos = e.GetPosition(DiagramCanvas);
|
||||||
|
CanvasTranslate.X += (newMousePos.X - mousePos.X) * CanvasScale.ScaleX;
|
||||||
|
CanvasTranslate.Y += (newMousePos.Y - mousePos.Y) * CanvasScale.ScaleY;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
private void LoadTemplates()
|
private void LoadTemplates()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -457,9 +530,13 @@ namespace NetworkDiagram
|
|||||||
{
|
{
|
||||||
_currentDiagram = new Diagram();
|
_currentDiagram = new Diagram();
|
||||||
DiagramCanvas.Children.Clear();
|
DiagramCanvas.Children.Clear();
|
||||||
|
// Re-add selection rectangle after clearing
|
||||||
|
if (SelectionRect != null) DiagramCanvas.Children.Add(SelectionRect);
|
||||||
|
|
||||||
_connectionLines.Clear();
|
_connectionLines.Clear();
|
||||||
_deviceElements.Clear();
|
_deviceElements.Clear();
|
||||||
ClearSelection();
|
ClearSelection();
|
||||||
|
CenterView();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SaveDiagram_Click(object sender, RoutedEventArgs e)
|
private void SaveDiagram_Click(object sender, RoutedEventArgs e)
|
||||||
@@ -497,15 +574,35 @@ namespace NetworkDiagram
|
|||||||
}
|
}
|
||||||
|
|
||||||
DiagramCanvas.Children.Clear();
|
DiagramCanvas.Children.Clear();
|
||||||
|
// Re-add selection rectangle after clearing
|
||||||
|
if (SelectionRect != null) DiagramCanvas.Children.Add(SelectionRect);
|
||||||
|
|
||||||
_connectionLines.Clear();
|
_connectionLines.Clear();
|
||||||
_deviceElements.Clear();
|
_deviceElements.Clear();
|
||||||
ClearSelection();
|
ClearSelection();
|
||||||
|
|
||||||
foreach (var device in _currentDiagram.Devices) RenderDevice(device);
|
foreach (var device in _currentDiagram.Devices) RenderDevice(device);
|
||||||
foreach (var conn in model.Connections.Select(cModel => new Connection { StartDevice = _currentDiagram.Devices[cModel.StartIndex], EndDevice = _currentDiagram.Devices[cModel.EndIndex], Type = cModel.Type }))
|
foreach (var conn in model.Connections.Select(cModel => new Connection { StartDevice = _currentDiagram.Devices[cModel.StartIndex], EndDevice = _currentDiagram.Devices[cModel.EndIndex], Type = cModel.Type }))
|
||||||
{
|
{
|
||||||
_currentDiagram.Connections.Add(conn);
|
_currentDiagram.Connections.Add(conn);
|
||||||
RenderConnection(conn);
|
RenderConnection(conn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Center on loaded content
|
||||||
|
if (_currentDiagram.Devices.Count > 0)
|
||||||
|
{
|
||||||
|
double minX = _currentDiagram.Devices.Min(d => d.X);
|
||||||
|
double minY = _currentDiagram.Devices.Min(d => d.Y);
|
||||||
|
double maxX = _currentDiagram.Devices.Max(d => d.X);
|
||||||
|
double maxY = _currentDiagram.Devices.Max(d => d.Y);
|
||||||
|
|
||||||
|
CanvasTranslate.X = -((minX + maxX) / 2) * CanvasScale.ScaleX + (CanvasViewport.ActualWidth / 2);
|
||||||
|
CanvasTranslate.Y = -((minY + maxY) / 2) * CanvasScale.ScaleY + (CanvasViewport.ActualHeight / 2);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CenterView();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void WireTool_Click(object sender, RoutedEventArgs e) => _activeTool = ConnectionType.Wire;
|
private void WireTool_Click(object sender, RoutedEventArgs e) => _activeTool = ConnectionType.Wire;
|
||||||
|
|||||||
Reference in New Issue
Block a user