"Infinite" workspace for diagrams

This commit is contained in:
Matěj Kubíček
2026-03-29 11:05:30 +02:00
parent b77d2f12aa
commit 2bd215c91a
2 changed files with 111 additions and 3 deletions
+14 -3
View File
@@ -73,12 +73,23 @@
</Border>
<!-- Canvas Area -->
<ScrollViewer Grid.Row="1" Grid.Column="1" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" Background="Transparent">
<Canvas x:Name="DiagramCanvas" Width="2000" Height="2000" AllowDrop="True"
<Grid Grid.Row="1" Grid.Column="1" x:Name="CanvasViewport" ClipToBounds="True" Background="Transparent"
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"
MouseDown="DiagramCanvas_MouseDown"
MouseMove="DiagramCanvas_MouseMove"
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>
<VisualBrush TileMode="Tile" Viewport="0,0,40,40" ViewportUnits="Absolute">
<VisualBrush.Visual>
@@ -122,6 +133,6 @@
<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"/>
</Canvas>
</ScrollViewer>
</Grid>
</Grid>
</Window>
+97
View File
@@ -30,12 +30,85 @@ namespace NetworkDiagram
private readonly Dictionary<Connection, Line> _connectionLines = new();
private readonly Dictionary<PlacedDevice, FrameworkElement> _deviceElements = new();
// Panning and Zooming
private Point _lastPanPoint;
private bool _isPanning;
public MainWindow()
{
InitializeComponent();
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()
{
try
@@ -457,9 +530,13 @@ namespace NetworkDiagram
{
_currentDiagram = new Diagram();
DiagramCanvas.Children.Clear();
// Re-add selection rectangle after clearing
if (SelectionRect != null) DiagramCanvas.Children.Add(SelectionRect);
_connectionLines.Clear();
_deviceElements.Clear();
ClearSelection();
CenterView();
}
private void SaveDiagram_Click(object sender, RoutedEventArgs e)
@@ -497,15 +574,35 @@ namespace NetworkDiagram
}
DiagramCanvas.Children.Clear();
// Re-add selection rectangle after clearing
if (SelectionRect != null) DiagramCanvas.Children.Add(SelectionRect);
_connectionLines.Clear();
_deviceElements.Clear();
ClearSelection();
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 }))
{
_currentDiagram.Connections.Add(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;