![]() |
VOOZH | about |
dotnet add package ApprenticeFoundryBlazor --version 23.4.0
NuGet\Install-Package ApprenticeFoundryBlazor -Version 23.4.0
<PackageReference Include="ApprenticeFoundryBlazor" Version="23.4.0" />
<PackageVersion Include="ApprenticeFoundryBlazor" Version="23.4.0" />Directory.Packages.props
<PackageReference Include="ApprenticeFoundryBlazor" />Project file
paket add ApprenticeFoundryBlazor --version 23.4.0
#r "nuget: ApprenticeFoundryBlazor, 23.4.0"
#:package ApprenticeFoundryBlazor@23.4.0
#addin nuget:?package=ApprenticeFoundryBlazor&version=23.4.0Install as a Cake Addin
#tool nuget:?package=ApprenticeFoundryBlazor&version=23.4.0Install as a Cake Tool
A comprehensive C# / Blazor diagramming library that combines 2D and 3D visualization capabilities for developers.
FoundryBlazor is a powerful diagramming and visualization library that brings together the best features of Visio, Three.js, and CesiumJS into the Blazor ecosystem. Originally demonstrated at NDC Oslo 2023, it's now available as a NuGet package supporting both Blazor Server and WebAssembly applications.
dotnet add package ApprenticeFoundryBlazor
Add FoundryBlazor services to your Program.cs:
using FoundryBlazor;
var builder = WebApplication.CreateBuilder(args);
// Add Blazor services
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor(); // For Blazor Server
// Add FoundryBlazor services
builder.Services.AddFoundryBlazorServices();
var app = builder.Build();
// Configure the HTTP request pipeline
app.UseStaticFiles(); // Essential for static assets
app.UseRouting();
app.MapBlazorHub();
app.MapFallbackToPage("/_Host");
app.Run();
Add these references to your _Host.cshtml (Blazor Server) or index.html (WebAssembly):
<link href="_content/ApprenticeFoundryBlazor/css/bootstrap/bootstrap.min.css" rel="stylesheet" />
<link href="_content/ApprenticeFoundryBlazor/css/site.css" rel="stylesheet" />
<link href="_content/Radzen.Blazor/css/material-base.css" rel="stylesheet" />
<script src="_content/Blazor.Extensions.Canvas/blazor.extensions.canvas.js"></script>
<script src="_content/ApprenticeFoundryBlazorThreeJS/js/blazor-three-js.js"></script>
<script src="_content/ApprenticeFoundryBlazor/js/app-lib.js"></script>
<script src="_content/Radzen.Blazor/Radzen.Blazor.js"></script>
@page "/canvas2d-demo"
@using FoundryBlazor.Shared
@using FoundryBlazor.Shape
@inject IWorkspace Workspace
<h3>2D Canvas Diagramming</h3>
<div class="container-fluid">
<div class="row">
<div class="col-3">
<div style="height: 600px; border: 1px solid #ccc;">
<ShapeTreeView />
</div>
</div>
<div class="col-9">
<Canvas2DComponent SceneName="MainCanvas2D"
CanvasWidth="1200"
CanvasHeight="600"
WithAnimations="true" />
</div>
</div>
</div>
<div class="mt-3">
<button class="btn btn-primary" @onclick="AddRectangle">Add Rectangle</button>
<button class="btn btn-success" @onclick="AddCircle">Add Circle</button>
<button class="btn btn-warning" @onclick="ClearCanvas">Clear Canvas</button>
</div>
@code {
protected override async Task OnInitializedAsync()
{
// Initialize with some default shapes
var drawing = Workspace.GetDrawing();
// Add a rectangle
var rect = new FoShape2D("Rectangle1", 120, 80, "Blue");
rect.PinX = 200;
rect.PinY = 150;
drawing?.AddShape(rect);
// Add a circle (using rectangle with equal width/height for simplicity)
var circle = new FoShape2D("Circle1", 100, 100, "Red");
circle.PinX = 400;
circle.PinY = 200;
drawing?.AddShape(circle);
await base.OnInitializedAsync();
}
private void AddRectangle()
{
var drawing = Workspace.GetDrawing();
var random = new Random();
var rect = new FoShape2D($"Rectangle_{DateTime.Now.Ticks}",
80 + random.Next(40),
60 + random.Next(40),
"Green");
rect.PinX = 100 + random.Next(800);
rect.PinY = 100 + random.Next(400);
drawing?.AddShape(rect);
StateHasChanged();
}
private void AddCircle()
{
var drawing = Workspace.GetDrawing();
var random = new Random();
var size = 60 + random.Next(40);
var circle = new FoShape2D($"Circle_{DateTime.Now.Ticks}", size, size, "Orange");
circle.PinX = 100 + random.Next(800);
circle.PinY = 100 + random.Next(400);
drawing?.AddShape(circle);
StateHasChanged();
}
private void ClearCanvas()
{
var drawing = Workspace.GetDrawing();
drawing?.ClearAll();
StateHasChanged();
}
}
@page "/canvas3d-demo"
@using FoundryBlazor.Shared
@using FoundryBlazor.Shape
@inject IWorkspace Workspace
<h3>3D Canvas Visualization</h3>
<div class="container-fluid">
<div class="row">
<div class="col-8">
<Canvas3DComponent SceneName="MainCanvas3D"
CanvasWidth="800"
CanvasHeight="600"
WithAnimations="true" />
</div>
<div class="col-4">
<div class="card">
<div class="card-header">
<h5>3D Controls</h5>
</div>
<div class="card-body">
<button class="btn btn-primary mb-2 w-100" @onclick="Add3DShape">Add 3D Shape</button>
<button class="btn btn-success mb-2 w-100" @onclick="AnimateShapes">Animate Shapes</button>
<button class="btn btn-warning mb-2 w-100" @onclick="ResetCamera">Reset Camera</button>
<button class="btn btn-danger mb-2 w-100" @onclick="Clear3DScene">Clear Scene</button>
<hr />
<h6>Camera Controls:</h6>
<p><small>
• Left Mouse: Rotate<br/>
• Right Mouse: Pan<br/>
• Scroll: Zoom
</small></p>
</div>
</div>
</div>
</div>
</div>
@code {
private int shapeCounter = 0;
protected override async Task OnInitializedAsync()
{
// Initialize 3D scene with some basic shapes
var arena = Workspace.GetArena();
// Add some initial 3D shapes programmatically
// Note: Actual 3D shape creation depends on your 3D shape classes
await base.OnInitializedAsync();
}
private void Add3DShape()
{
var arena = Workspace.GetArena();
var random = new Random();
// Example of adding 3D shapes (adjust based on your actual 3D shape classes)
shapeCounter++;
// This is conceptual - replace with actual 3D shape creation
// var shape3D = new FoShape3D($"Shape3D_{shapeCounter}");
// shape3D.Position = new Vector3(
// random.Next(-5, 5),
// random.Next(-5, 5),
// random.Next(-5, 5)
// );
// arena?.AddShape(shape3D);
StateHasChanged();
}
private void AnimateShapes()
{
// Trigger animations on 3D shapes
// Implementation depends on your animation system
StateHasChanged();
}
private void ResetCamera()
{
// Reset 3D camera to default position
// Implementation depends on your camera system
StateHasChanged();
}
private void Clear3DScene()
{
var arena = Workspace.GetArena();
arena?.ClearAll();
StateHasChanged();
}
}
@page "/combined-canvas"
@using FoundryBlazor.Shared
@using FoundryBlazor.Shape
@inject IWorkspace Workspace
<h3>Combined 2D & 3D Visualization</h3>
<div class="container-fluid">
<div class="row">
<div class="col-2">
<div style="height: 500px;">
<RadzenShapeTreeView />
</div>
</div>
<div class="col-5">
<h5>2D Diagram</h5>
<Canvas2DComponent SceneName="Combined2D"
CanvasWidth="500"
CanvasHeight="400"
WithAnimations="true" />
</div>
<div class="col-5">
<h5>3D Visualization</h5>
<Canvas3DComponent SceneName="Combined3D"
CanvasWidth="500"
CanvasHeight="400"
WithAnimations="true" />
</div>
</div>
<div class="row mt-3">
<div class="col-12">
<button class="btn btn-primary me-2" @onclick="SyncViews">Sync 2D → 3D</button>
<button class="btn btn-success me-2" @onclick="AddToBoth">Add to Both</button>
<button class="btn btn-warning me-2" @onclick="ClearBoth">Clear Both</button>
</div>
</div>
</div>
@code {
private void SyncViews()
{
// Example: Convert 2D shapes to 3D representation
var drawing = Workspace.GetDrawing();
var arena = Workspace.GetArena();
// Implementation would depend on your specific shape conversion logic
// This demonstrates the concept of syncing between 2D and 3D
StateHasChanged();
}
private void AddToBoth()
{
var drawing = Workspace.GetDrawing();
var arena = Workspace.GetArena();
var random = new Random();
// Add to 2D
var rect2D = new FoShape2D($"Synced2D_{DateTime.Now.Ticks}", 100, 60, "Purple");
rect2D.PinX = 100 + random.Next(300);
rect2D.PinY = 100 + random.Next(200);
drawing?.AddShape(rect2D);
// Add corresponding 3D shape
// var shape3D = new FoShape3D($"Synced3D_{DateTime.Now.Ticks}");
// arena?.AddShape(shape3D);
StateHasChanged();
}
private void ClearBoth()
{
Workspace.GetDrawing()?.ClearAll();
Workspace.GetArena()?.ClearAll();
StateHasChanged();
}
}
@using BlazorComponentBus
@using FoundryBlazor.PubSub
@inject ComponentBus PubSub
@implements IDisposable
<Canvas2DComponent SceneName="EventCanvas" />
@code {
protected override async Task OnInitializedAsync()
{
// Subscribe to shape selection events
PubSub.SubscribeTo<ShapeUIEvent>(OnShapeSelected);
// Subscribe to refresh events
PubSub.SubscribeTo<RefreshUIEvent>(OnUIRefresh);
await base.OnInitializedAsync();
}
private async Task OnShapeSelected(ShapeUIEvent eventArgs)
{
Console.WriteLine($"Shape selected: {eventArgs.Shape?.Name}");
// Handle shape selection logic
StateHasChanged();
}
private async Task OnUIRefresh(RefreshUIEvent eventArgs)
{
// Handle UI refresh
StateHasChanged();
}
public void Dispose()
{
PubSub?.UnSubscribeFrom<ShapeUIEvent>(OnShapeSelected);
PubSub?.UnSubscribeFrom<RefreshUIEvent>(OnUIRefresh);
}
}
// Create custom 2D shapes
public class CustomShape : FoShape2D
{
public CustomShape(string name) : base(name, "CustomColor")
{
// Custom initialization
Width = 150;
Height = 100;
}
// Override drawing behavior if needed
// Custom rendering logic can be implemented here
}
// Usage in component
private void AddCustomShape()
{
var drawing = Workspace.GetDrawing();
var customShape = new CustomShape("MyCustomShape");
customShape.PinX = 300;
customShape.PinY = 200;
drawing?.AddShape(customShape);
}
// For large datasets, disable animations
<Canvas2DComponent SceneName="LargeDataset"
CanvasWidth="1200"
CanvasHeight="800"
WithAnimations="false" />
// Batch operations for better performance
private void AddMultipleShapes()
{
var drawing = Workspace.GetDrawing();
// Batch add multiple shapes
for (int i = 0; i < 100; i++)
{
var shape = new FoShape2D($"BatchShape_{i}", 50, 30, "Blue");
shape.PinX = (i % 10) * 60 + 50;
shape.PinY = (i / 10) * 40 + 50;
drawing?.AddShape(shape);
}
// Trigger single refresh instead of multiple
StateHasChanged();
}
Canvas not rendering:
app.UseStaticFiles() is called in Program.csServices not found:
Unable to resolve service for type 'FoundryBlazor.IWorkspace'
Solution: Add services.AddFoundryBlazorServices() to Program.cs
Static assets not loading:
_content/ApprenticeFoundryBlazor/ path is accessibleVisit our interactive demo: https://apprenticefoundry.github.io/
FoundryBlazor uses a compatibility wrapper architecture for 3D matrix operations:
FoVector3D (double precision) and Vector3 (float precision)ApprenticeFoundryBlazorThis project is licensed under the MIT License - see the file for details.
Contributions are welcome! Please feel free to submit pull requests or open issues for bugs and feature requests.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net9.0 net9.0 is compatible. net9.0-android net9.0-android was computed. net9.0-browser net9.0-browser was computed. net9.0-ios net9.0-ios was computed. net9.0-maccatalyst net9.0-maccatalyst was computed. net9.0-macos net9.0-macos was computed. net9.0-tvos net9.0-tvos was computed. net9.0-windows net9.0-windows was computed. net10.0 net10.0 was computed. net10.0-android net10.0-android was computed. net10.0-browser net10.0-browser was computed. net10.0-ios net10.0-ios was computed. net10.0-maccatalyst net10.0-maccatalyst was computed. net10.0-macos net10.0-macos was computed. net10.0-tvos net10.0-tvos was computed. net10.0-windows net10.0-windows was computed. |
Showing the top 1 NuGet packages that depend on ApprenticeFoundryBlazor:
| Package | Downloads |
|---|---|
|
SparingEngine
Package Description |
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 23.4.0 | 306 | 9/25/2025 |
| 23.3.0 | 273 | 9/24/2025 |
| 23.2.0 | 256 | 9/22/2025 |
| 23.1.0 | 256 | 9/22/2025 |
| 23.0.0 | 299 | 9/22/2025 |
| 21.3.0 | 269 | 9/13/2025 |
| 21.2.0 | 224 | 9/13/2025 |
| 21.0.0 | 275 | 9/10/2025 |
| 20.3.0 | 283 | 8/11/2025 |
| 20.2.0 | 209 | 8/10/2025 |
| 20.0.0 | 223 | 8/9/2025 |
| 19.0.0 | 263 | 7/28/2025 |
| 18.3.0 | 436 | 3/29/2025 |
| 18.2.0 | 263 | 3/2/2025 |
| 18.1.1 | 236 | 3/2/2025 |
| 18.1.0 | 227 | 3/2/2025 |
| 18.0.0 | 270 | 2/23/2025 |
| 17.4.1 | 231 | 2/11/2025 |
| 17.4.0 | 255 | 2/9/2025 |
| 17.3.0 | 232 | 1/31/2025 |