WPF Chart - Examples
SciChart WPF ships with hundreds of WPF Chart Examples which you can browse, play with, view the source-code and even export each WPF Chart Example to a stand-alone Visual Studio solution. All of this is possible with the new and improved SciChart WPF Examples Suite, which ships as part of the SciChart WPF SDK.
This 3D Chart demo shows how to add a custom Cube Geometry to the SciChart3DSurface using our BaseSceneEntity type
Entities may be added to the SciChart3DSurface.Viewport3D.RootEntity, which is a type of BaseSceneEntity. Several cubes are created and added to the scene.
We also use the same example to demonstrate drawing 3D Text (Billboarded, facing camera always) at specific locations in the 3D scene. This uses the Font3D type and again BaseSceneEntity to custom draw text of a specific font, color and string in the 3D scene.
The C#/WPF source code for the WPF 3D Chart Add Geometry to a 3D Scene example is included below (Scroll down!).
Did you know you can also view the source code from one of the following sources as well?
- Clone the SciChart.WPF.Examples from Github.
- Or, view source in the SciChart WPF Examples suite.
- Also the SciChart WPF Trial contains the full source for the examples (link below).
AddObjectsToA3DChart.xaml
View source code<UserControl x:Class="SciChart.Examples.Examples.Charts3D.Customize3DChart.AddGeometry3D.AddGeometryTo3DChart"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:s3D="http://schemas.abtsoftware.co.uk/scichart3D"
mc:Ignorable="d"
d:DesignHeight="400" d:DesignWidth="600">
<Grid>
<s3D:SciChart3DSurface x:Name="sciChart3DSurface"
WorldDimensions="400,400,400"
BorderThickness="0"
s3D:ZxAxisPlane.AxisPlaneVisibilityMode="NegativeSide"
s3D:XyAxisPlane.IsPlaneVisible="False"
s3D:ZyAxisPlane.IsPlaneVisible="False">
<s3D:SciChart3DSurface.Camera>
<s3D:Camera3D Target="0,50,0" Position="-400,200,400" />
</s3D:SciChart3DSurface.Camera>
<s3D:SciChart3DSurface.XAxis>
<s3D:NumericAxis3D/>
</s3D:SciChart3DSurface.XAxis>
<s3D:SciChart3DSurface.YAxis>
<s3D:NumericAxis3D />
</s3D:SciChart3DSurface.YAxis>
<s3D:SciChart3DSurface.ZAxis>
<s3D:NumericAxis3D/>
</s3D:SciChart3DSurface.ZAxis>
<s3D:SciChart3DSurface.ChartModifier>
<s3D:ModifierGroup3D>
<s3D:FreeLookModifier3D ExecuteOn="MouseLeftButton" ExecuteWhen="Shift"/>
<s3D:OrbitModifier3D ExecuteOn="MouseLeftButton"/>
<s3D:MouseWheelZoomModifier3D/>
<s3D:ZoomExtentsModifier3D ResetPosition="-400,200,400" ResetTarget="0,50,0" AnimateDurationMs="500"/>
</s3D:ModifierGroup3D>
</s3D:SciChart3DSurface.ChartModifier>
</s3D:SciChart3DSurface>
</Grid>
</UserControl>
AddObjectsToA3DChart.xaml.cs
View source code// *************************************************************************************
// SCICHART® Copyright SciChart Ltd. 2011-2025. All rights reserved.
//
// Web: http://www.scichart.com
// Support: support@scichart.com
// Sales: sales@scichart.com
//
// AddGeometryTo3DChart.xaml.cs is part of the SCICHART® Examples. Permission is hereby granted
// to modify, create derivative works, distribute and publish any part of this source
// code whether for commercial, private or personal use.
//
// The SCICHART® examples are distributed in the hope that they will be useful, but
// without any warranty. It is provided "AS IS" without warranty of any kind, either
// expressed or implied.
// *************************************************************************************
using System.Windows.Controls;
using System.Windows.Media;
using SciChart.Charting3D;
using SciChart.Charting3D.Interop;
namespace SciChart.Examples.Examples.Charts3D.Customize3DChart.AddGeometry3D
{
public partial class AddGeometryTo3DChart : UserControl
{
public AddGeometryTo3DChart()
{
InitializeComponent();
Loaded += OnExampleLoaded;
}
private void OnExampleLoaded(object sender, System.Windows.RoutedEventArgs e)
{
// Create some SceneInstances and add to the 3D scene
// Requires Coordinate transformation from Data Coordinates to World Coordinates,
// which requires Axes3D to be created
CreateCubes();
CreateLabels();
}
private void CreateLabels()
{
// Add text labels to the scene
var textA = new TextSceneEntity("Teeny Block", Color.FromArgb(255, 0xDC, 0x79, 0x69), TransformToWorldCoordinates(new Vector3(6.5f, 2.0f, 6.5f)), 9, "Segoe UI");
var textB = new TextSceneEntity("Big Block", Color.FromArgb(255, 0xF4, 0x84, 0x0B), TransformToWorldCoordinates(new Vector3(4.0f, 4.0f, 4.0f)), 10, "Tahoma");
sciChart3DSurface.Viewport3D.RootEntity.Children.Add(textA);
sciChart3DSurface.Viewport3D.RootEntity.Children.Add(textB);
}
private Vector3 TransformToWorldCoordinates(Vector3 chartPoint3D)
{
var worldPoint3D = new Vector3
{
X = (float)sciChart3DSurface.XAxis.GetCurrentCoordinateCalculator().GetCoordinate(chartPoint3D.X) - sciChart3DSurface.WorldDimensions.X / 2.0f,
Y = (float)sciChart3DSurface.YAxis.GetCurrentCoordinateCalculator().GetCoordinate(chartPoint3D.Y),
Z = (float)sciChart3DSurface.ZAxis.GetCurrentCoordinateCalculator().GetCoordinate(chartPoint3D.Z) - sciChart3DSurface.WorldDimensions.Z / 2.0f
};
return worldPoint3D;
}
private void CreateCubes()
{
// Create a cubes in 3D World space with TopLeft and BottomRight coordinates
// We set some cubes to transparent colors to demonstrate Order Independent Transparency
// Create CubeGeometries defined by chart coordinates
var cubeA = CreateCubeGeometry(new Vector3(4.5f, 0.0f, 4.5f), new Vector3(6.0f, 1.5f, 6.0f), Color.FromArgb(128, 0x64, 0xBA, 0xE4));
var cubeB = CreateCubeGeometry(new Vector3(6.0f, 0.0f, 6.0f), new Vector3(7.0f, 2.5f, 7.0f), Color.FromArgb(128, 0xDC, 0x79, 0x69));
var cubeC = CreateCubeGeometry(new Vector3(7.0f, 0.0f, 7.0f), new Vector3(8.0f, 3.5f, 8.0f), Color.FromArgb(255, 0x6B, 0xC4, 0xA9));
var cubeD = CreateCubeGeometry(new Vector3(2.5f, 0.0f, 2.5f), new Vector3(4.5f, 3.5f, 4.5f), Color.FromArgb(255, 0x5C, 0x43, 0x9B));
var cubeE = CreateCubeGeometry(new Vector3(1.0f, 0.0f, 1.0f), new Vector3(6.0f, 5.0f, 6.0f), Color.FromArgb(128, 0xF4, 0x84, 0x0B));
var cubeF = CreateCubeGeometry(new Vector3(1.0f, 0.0f, 1.0f), new Vector3(2.5f, 1.5f, 2.5f), Color.FromArgb(128, 0xF6, 0x08, 0x6C));
// force a far position on this cube, in world coordinates ( user can do this in cases where a box inside another )
var farPosition = new TSRVector3(20000.0f, 20000.0f, 2000.0f);
cubeF.SetPosition(farPosition);
// Add the cubes to the 3D Scene
sciChart3DSurface.Viewport3D.RootEntity.Children.Add(cubeA);
sciChart3DSurface.Viewport3D.RootEntity.Children.Add(cubeB);
sciChart3DSurface.Viewport3D.RootEntity.Children.Add(cubeC);
sciChart3DSurface.Viewport3D.RootEntity.Children.Add(cubeD);
sciChart3DSurface.Viewport3D.RootEntity.Children.Add(cubeE);
sciChart3DSurface.Viewport3D.RootEntity.Children.Add(cubeF);
}
private CubeGeometry CreateCubeGeometry(Vector3 topLeft, Vector3 bottomRight, Color color)
{
// Transform the chart coordinates to world coordinates
var topLeftWorld = TransformToWorldCoordinates(topLeft);
var bottomRightWorld = TransformToWorldCoordinates(bottomRight);
// Create a CubeGeometry from the world coordinates
var cubeGeometry = new CubeGeometry(topLeftWorld, bottomRightWorld, color);
return cubeGeometry;
}
}
}
CubeGeometry.cs
View source code// *************************************************************************************
// SCICHART® Copyright SciChart Ltd. 2011-2025. All rights reserved.
//
// Web: http://www.scichart.com
// Support: support@scichart.com
// Sales: sales@scichart.com
//
// CubeGeometry.cs is part of the SCICHART® Examples. Permission is hereby granted
// to modify, create derivative works, distribute and publish any part of this source
// code whether for commercial, private or personal use.
//
// The SCICHART® examples are distributed in the hope that they will be useful, but
// without any warranty. It is provided "AS IS" without warranty of any kind, either
// expressed or implied.
// *************************************************************************************
using System.Linq;
using System.Windows.Media;
using SciChart.Charting3D;
using SciChart.Charting3D.Interop;
using SciChart.Charting3D.Primitives;
namespace SciChart.Examples.Examples.Charts3D.Customize3DChart.AddGeometry3D
{
/// <summary>
/// A class to demonstrate a 3D Geometry added to the SciChart3D Scene. Created using our BaseSceneEntity and Mesh APIs
/// </summary>
public class CubeGeometry : BaseSceneEntity<SCRTSceneEntity>
{
private readonly Vector3 _bottomRight;
private readonly Vector3 _topLeft;
private readonly Color _cubeColor;
/// <summary>
/// Creates a representation of a Cube in the 3D space, defined in world coordinates
/// </summary>
/// <param name="topLeft">Point in the 3D space that determines the top-left corner of a cube</param>
/// <param name="bottomRight">Point in the 3D space that determines the bottom-right corner of a cube</param>
/// <param name="cubeColor">Color of the cube surface</param>
public CubeGeometry(Vector3 topLeft, Vector3 bottomRight, Color cubeColor) : base(new SCRTSceneEntity())
{
// Setting the position of scene entities will be used back when sorting them from camera perspective back to front
using (TSRVector3 centerPosition = new TSRVector3(
0.5f*(topLeft.x + bottomRight.x),
0.5f*(topLeft.y + bottomRight.y),
0.5f*(topLeft.z + bottomRight.z)))
{
SetPosition(centerPosition);
}
this._topLeft = topLeft;
this._bottomRight = bottomRight;
this._cubeColor = cubeColor;
}
/// <summary>
/// Determines a kind of the entity. If SCRT_SCENE_ENTITY_KIND_TRANSPARENT then the 3D Engine must make some internal adjustments to allow order independent transparency
/// </summary>
public override eSCRTSceneEntityKind GetKind()
{
return _cubeColor.A == 255 ? eSCRTSceneEntityKind.SCRT_SCENE_ENTITY_KIND_OPAQUE : eSCRTSceneEntityKind.SCRT_SCENE_ENTITY_KIND_TRANSPARENT;
}
/// <summary>
/// Called when the 3D Engine wishes to render this element. This is where geometry must be drawn to the 3D scene
/// </summary>
/// <param name="rpi">The <see cref="IRenderPassInfo3D" /> containing parameters for the current render pass.</param>
public override void RenderScene(IRenderPassInfo3D rpi)
{
float bottomRightCoordX = _bottomRight.X;
float bottomRightCoordY = _bottomRight.Y;
float bottomRightCoordZ = _bottomRight.Z;
float topLeftCoordX = _topLeft.X;
float topLeftCoordY = _topLeft.Y;
float topLeftCoordZ = _topLeft.Z;
// y 1--------0
// | /| /|
// | 5--------4 |
// | | | | |
// | | | | |
// | | 2--------3
// | z | / |/
// | / 6--------7
// |/
// ----------- X
Vector3[] corners = {
new Vector3(topLeftCoordX, topLeftCoordY, topLeftCoordZ), //0
new Vector3(bottomRightCoordX, topLeftCoordY, topLeftCoordZ), //1
new Vector3(bottomRightCoordX, bottomRightCoordY, topLeftCoordZ), //2
new Vector3(topLeftCoordX, bottomRightCoordY, topLeftCoordZ), //3
new Vector3(topLeftCoordX, topLeftCoordY, bottomRightCoordZ), //4
new Vector3(bottomRightCoordX, topLeftCoordY, bottomRightCoordZ), //5
new Vector3(bottomRightCoordX, bottomRightCoordY, bottomRightCoordZ), //6
new Vector3(topLeftCoordX, bottomRightCoordY, bottomRightCoordZ), //7
};
Vector3[] normals = {
new Vector3(+0.0f, +0.0f, -1.0f), //front
new Vector3(+0.0f, +0.0f, +1.0f), //back
new Vector3(+1.0f, +0.0f, +0.0f), //right
new Vector3(-1.0f, +0.0f, +0.0f), //left
new Vector3(+0.0f, +1.0f, +0.0f), //top
new Vector3(+0.0f, -1.0f, +0.0f), //bottom
};
eSCRTUpAxis upaxis = VXccelEngine3D.GetUpAxis();
// We create a mesh context. There are various mesh render modes. The simplest is Triangles
// For this mode we have to draw a single triangle (three vertices) for each corner of the cube
using (var meshContext = BeginLitMesh(TSRRenderMode.TRIANGLES))
{
// Set the Rasterizer State for this entity
if (upaxis == eSCRTUpAxis.Z_UP)
{
VXccelEngine3D.PushRasterizerState(RasterizerStates.Default.TSRRasterizerState);
}
else
{
VXccelEngine3D.PushRasterizerState(RasterizerStates.CullBackFacesState.TSRRasterizerState);
}
// Set the color before drawing vertices
meshContext.SetVertexColor(_cubeColor);
// Pass Entity ID value for a hit test purpose
ulong selectionColor = VXccelEngine3D.EncodeSelectionId(EntityId, 0);
meshContext.SetSelectionId(selectionColor);
// Now draw the triangles. Each face of the cube is made up of two triangles
// Front face
SetNormal(meshContext, normals[0]);
SetVertex(meshContext, corners[0]);
SetVertex(meshContext, corners[2]);
SetVertex(meshContext, corners[1]);
SetVertex(meshContext, corners[2]);
SetVertex(meshContext, corners[0]);
SetVertex(meshContext, corners[3]);
// Right side face
SetNormal(meshContext, normals[2]);
SetVertex(meshContext, corners[1]);
SetVertex(meshContext, corners[2]);
SetVertex(meshContext, corners[6]);
SetVertex(meshContext, corners[1]);
SetVertex(meshContext, corners[6]);
SetVertex(meshContext, corners[5]);
// Top face
SetNormal(meshContext, normals[4]);
SetVertex(meshContext, corners[2]);
SetVertex(meshContext, corners[7]);
SetVertex(meshContext, corners[6]);
SetVertex(meshContext, corners[7]);
SetVertex(meshContext, corners[2]);
SetVertex(meshContext, corners[3]);
// Left side face
SetNormal(meshContext, normals[3]);
SetVertex(meshContext, corners[3]);
SetVertex(meshContext, corners[0]);
SetVertex(meshContext, corners[4]);
SetVertex(meshContext, corners[3]);
SetVertex(meshContext, corners[4]);
SetVertex(meshContext, corners[7]);
// Back face
SetNormal(meshContext, normals[1]);
SetVertex(meshContext, corners[7]);
SetVertex(meshContext, corners[5]);
SetVertex(meshContext, corners[6]);
SetVertex(meshContext, corners[7]);
SetVertex(meshContext, corners[4]);
SetVertex(meshContext, corners[5]);
// Bottom face
SetNormal(meshContext, normals[5]);
SetVertex(meshContext, corners[0]);
SetVertex(meshContext, corners[1]);
SetVertex(meshContext, corners[5]);
SetVertex(meshContext, corners[0]);
SetVertex(meshContext, corners[5]);
SetVertex(meshContext, corners[4]);
}
// Revert raster state
VXccelEngine3D.PopRasterizerState();
// Set the Rasterizer State for the cube mesh wireframe
VXccelEngine3D.PushRasterizerState(RasterizerStates.WireframeState.TSRRasterizerState);
// Create a Line Context for a continuous line and draw the outline of the cube
var lineColor = Color.FromArgb(0xFF, _cubeColor.R, _cubeColor.G, _cubeColor.B);
CreateSquare(2.0f, true, lineColor, new[] { corners[0], corners[1], corners[2], corners[3] });
CreateSquare(2.0f, true, lineColor, new[] { corners[4], corners[5], corners[6], corners[7] });
CreateSquare(2.0f, true, lineColor, new[] { corners[0], corners[4], corners[7], corners[3] });
CreateSquare(2.0f, true, lineColor, new[] { corners[5], corners[1], corners[2], corners[6] });
// Revert raster state
VXccelEngine3D.PopRasterizerState();
}
private void CreateSquare(float lineThickness, bool isAntiAlias, Color lineColor, Vector3[] vertices)
{
using (var lineContext = BeginLineStrips(lineThickness, isAntiAlias))
{
lineContext.SetVertexColor(lineColor);
// Pass Entity ID value for a hit test purpose
ulong selectionColor = VXccelEngine3D.EncodeSelectionId(EntityId, 0);
lineContext.SetSelectionId(selectionColor);
foreach (var v in vertices)
{
SetVertex(lineContext, v);
}
SetVertex(lineContext, vertices.First());
lineContext.Freeze();
lineContext.Draw();
}
}
private void SetVertex(IImmediateLitMeshContext meshContext, Vector3 vector3)
{
meshContext.SetVertex3(vector3.X, vector3.Y, vector3.Z);
}
private void SetVertex(ILinesMesh linesContext, Vector3 vector3)
{
linesContext.SetVertex3(vector3.X, vector3.Y, vector3.Z);
}
private void SetNormal(IImmediateLitMeshContext meshContext, Vector3 vector3)
{
meshContext.Normal3(vector3.X, vector3.Y, vector3.Z);
}
}
}TextSceneEntity.cs
View source code// *************************************************************************************
// SCICHART® Copyright SciChart Ltd. 2011-2025. All rights reserved.
//
// Web: http://www.scichart.com
// Support: support@scichart.com
// Sales: sales@scichart.com
//
// TextSceneEntity.cs is part of the SCICHART® Examples. Permission is hereby granted
// to modify, create derivative works, distribute and publish any part of this source
// code whether for commercial, private or personal use.
//
// The SCICHART® examples are distributed in the hope that they will be useful, but
// without any warranty. It is provided "AS IS" without warranty of any kind, either
// expressed or implied.
// *************************************************************************************
using System;
using System.ComponentModel;
using System.Windows.Media;
using SciChart.Charting3D;
using SciChart.Charting3D.Interop;
using SciChart.Charting3D.Primitives;
using SciChart.Charting3D.Visuals.Primitives;
using SciChart.Core.Extensions;
namespace SciChart.Examples.Examples.Charts3D.Customize3DChart.AddGeometry3D
{
/// <summary>
/// A class to demonstrate a 3D Text Elements added to the SciChart3D Scene. Created using our BaseSceneEntity and Font3D APIs
/// </summary>
public class TextSceneEntity : BaseSceneEntity<SCRTSceneEntity>
{
private readonly Color _textColor;
private readonly int _fontSize;
private readonly string _fontFamily;
private Font3D _font;
public string Text { get; set; }
[TypeConverter(typeof(StringToVector3TypeConverter))]
public Vector3 Location { get; set; }
static TextSceneEntity()
{
SCRTDllLoader.InitNativeLibs();
}
/// <summary>
/// Creates a representation of a Text Label in the 3D space, defined in world coordinates
/// </summary>
/// <param name="location">Point in the 3D space that determines the top-left corner of a label</param>
public TextSceneEntity(string text, Color textColor, Vector3 location,
int fontSize = 8, string fontFamily = "Arial")
: base(new SCRTSceneEntity())
{
Text = text;
_textColor = textColor;
Location = location;
_fontSize = fontSize;
_fontFamily = fontFamily;
// Set requires SelectionId to False to exclude this item from selection, tooltips and also
// prevent issues with maximum number of selectable meshes
RequiresSelectionId = false;
}
/// <summary>
/// Called when the 3D Engine wishes to render this element. This is where geometry must be drawn to the 3D scene
/// </summary>
/// <param name="rpi">The <see cref="IRenderPassInfo3D" /> containing parameters for the current render pass.</param>
public override void RenderScene(IRenderPassInfo3D rpi)
{
var currentCamera = RootSceneEntity?.Viewport3D.CameraController;
if (currentCamera == null || Location == null) return;
// Display billboarded text using camera vectors
Vector3 cameraFwd = currentCamera.Forward;
Vector3 cameraUp = currentCamera.Up;
// Compute a side vector
Vector3 cameraSide = cameraFwd ^ cameraUp;
cameraSide.Normalize();
// Compute orthogonal up vector
cameraUp = cameraSide ^ cameraFwd;
cameraUp.Normalize();
// Display text billboarded using camera side, up vectors (text will always face camera)
_font.BeginBillboard(cameraSide, cameraUp);
_font.AddText(Text, _textColor, Location.X, Location.Y, Location.Z);
_font.End();
}
/// <summary>
/// Called when the 3D Engine wishes to update the geometry in this element. This is where we need to cache geometry before draw.
/// </summary>
/// <param name="rpi">The <see cref="IRenderPassInfo3D" /> containing parameters for the current render pass.</param>
public override void UpdateScene(IRenderPassInfo3D rpi)
{
_font ??= new Font3D(_fontFamily, (uint)_fontSize);
}
/// <summary>
/// Called when the D3DEngine Restarts. Meshes and DirectX related objects should be recreated
/// </summary>
public override void OnEngineRestart()
{
DisposeFont();
base.OnEngineRestart();
}
private void DisposeFont()
{
_font.SafeDispose();
_font = null;
}
public override eSCRTSceneEntityKind GetKind()
{
return eSCRTSceneEntityKind.SCRT_SCENE_ENTITY_KIND_HUD3D;
}
/// <summary>
/// Releases unmanaged and - optionally - managed resources.
/// </summary>
protected override void Dispose(bool disposing)
{
if (disposing)
{
DisposeFont();
}
base.Dispose(disposing);
}
}
}Back to WPF Chart Examples


