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.
The WPF 3D Realtime Surface Mesh example shows how to update a UniformGridDataSeries3D in real-time to render to a 3D SurfaceMeshRenderableSeries3D.
The Surface Mesh colors and palette can be udpated by supplying a MeshColorPalette, which wraps a LinearGradientBrush, or a Texture.
Many mesh options are available such as Wireframe, SolidMesh, Solid with Wireframe, and many paletting options such as Solid Cells, Interpolated Cells and Textured.
Change some of the options on the left-hand side to see what this versatile chart type is capable of doing.
The C#/WPF source code for the WPF 3D Chart Realtime 3D Surface Mesh Plot Example 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).
CreateRealTime3DUniformMeshChart.xaml
View source code<UserControl x:Class="SciChart.Examples.Examples.Charts3D.CreateRealtime3DCharts.CreateRealTime3DSurfaceMeshChart"
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:s3D="http://schemas.abtsoftware.co.uk/scichart3D"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:ext="http://schemas.abtsoftware.co.uk/scichart/exampleExternals"
xmlns:s="http://schemas.abtsoftware.co.uk/scichart"
xmlns:system="clr-namespace:System;assembly=mscorlib"
mc:Ignorable="d"
d:DesignHeight="400"
d:DesignWidth="600">
<UserControl.Resources>
<!-- Used for this example purposes, when switching color map, switch the ColorMapTextureSize -->
<Style x:Key="SurfaceMeshStyle" TargetType="{x:Type s3D:SurfaceMeshRenderableSeries3D}">
<!-- The ColorMapTextureSize property defines how large the texture is when a ColorMap is applied -->
<!-- For height color maps, 1024x1 is plenty of resolution to give a nice interpolated color map -->
<!-- This is also the default value -->
<Setter Property="ColorMapTextureSize" Value="1024,1"/>
<Style.Triggers>
<!-- However when an image is applied, we want a nice square texture. -->
<!-- to set the ColorMapTextureSize to 512, 512 -->
<DataTrigger Binding="{Binding Source={x:Reference Name=ColorMapCombo}, Path=SelectedIndex}" Value="2">
<Setter Property="ColorMapTextureSize" Value="512,512"/>
</DataTrigger>
</Style.Triggers>
</Style>
<!-- Simulates a Linear Interpolated color map -->
<s3D:GradientColorPalette x:Key="DefaultMeshColorPalette" IsStepped="False">
<s3D:GradientColorPalette.GradientStops>
<GradientStop Offset="1" Color="DarkRed"/>
<GradientStop Offset="0.9" Color="Red"/>
<GradientStop Offset="0.7" Color="Yellow"/>
<GradientStop Offset="0.5" Color="GreenYellow"/>
<GradientStop Offset="0.3" Color="Cyan"/>
<GradientStop Offset="0.1" Color="Blue"/>
<GradientStop Offset="0.0" Color="#1D2C6B"/>
</s3D:GradientColorPalette.GradientStops>
</s3D:GradientColorPalette>
<ext:LinearToLogarithmicValueConverter x:Key="LinearToLogarithmicValueConverter"/>
</UserControl.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<ext:SciChart3DInteractionToolbar TargetSurface="{Binding Source={x:Reference Name=SciChart3DSurface}}">
<ext:SciChart3DInteractionToolbar.Resources>
<Style TargetType="{x:Type ComboBox}" BasedOn="{StaticResource {x:Type ComboBox}}">
<Setter Property="MinHeight" Value="26"/>
<Setter Property="Margin" Value="3,5"/>
<Setter Property="FontSize" Value="11"/>
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock Text="{Binding}" Margin="5,0" VerticalAlignment="Center"/>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</ext:SciChart3DInteractionToolbar.Resources>
<ext:FlyoutSeparator Background="#444"/>
<ToggleButton x:Name="StartButton" Click="StartButton_Click" Style="{StaticResource PlayButtonStyle}" Padding="4"/>
<ToggleButton x:Name="PauseButton" Click="PauseButton_Click" Style="{StaticResource PauseButtonStyle}" Padding="7"/>
<ext:FlyoutSeparator Background="#444"/>
<ext:FlyoutMenuButton Style="{StaticResource FlyoutMenuButtonStyle}" Content="MESH" FontSize="9" Padding="0">
<ext:FlyoutMenuButton.PopupContent>
<StackPanel Orientation="Vertical" MinWidth="150">
<TextBlock FontSize="12" Text="Opacity"/>
<Slider Minimum="0.2" Maximum="1.0" x:Name="opacitySlider" Value="0.9" Margin="4" Orientation="Horizontal" HorizontalAlignment="Stretch"/>
<TextBlock FontSize="12" Text="Normal hardness"/>
<Slider Minimum="0.0" Maximum="1.0" x:Name="normalHardnessSlider" Value="1.0" Margin="4" Orientation="Horizontal" HorizontalAlignment="Stretch"/>
<TextBlock FontSize="12" Text="Shininess"/>
<Slider Minimum="0" Maximum="11" x:Name="shininessSlider" Margin="4" Orientation="Horizontal" HorizontalAlignment="Stretch"/>
<TextBlock Text="Draw Mesh As" FontSize="12"/>
<ComboBox x:Name="DrawMeshAsCombo" SelectedIndex="0">
<ComboBox.Items>
<s3D:DrawMeshAs>SolidWireFrame</s3D:DrawMeshAs>
<s3D:DrawMeshAs>SolidMesh</s3D:DrawMeshAs>
<s3D:DrawMeshAs>Wireframe</s3D:DrawMeshAs>
</ComboBox.Items>
</ComboBox>
</StackPanel>
</ext:FlyoutMenuButton.PopupContent>
</ext:FlyoutMenuButton>
<ext:FlyoutMenuButton Style="{StaticResource FlyoutMenuButtonStyle}" Content="COLOR" FontSize="7" Padding="0">
<ext:FlyoutMenuButton.PopupContent>
<StackPanel Orientation="Vertical" MinWidth="150">
<TextBlock Text="Choose a Color Map" FontSize="12"/>
<ComboBox x:Name="ColorMapCombo" SelectedIndex="0" SelectionChanged="ColorMapCombo_OnSelectionChanged">
<ComboBox.ItemTemplate>
<DataTemplate DataType="{x:Type s3D:GradientColorPalette}">
<Rectangle Width="16" Height="20" Margin="2" Fill="{Binding Brush}">
<Rectangle.LayoutTransform>
<RotateTransform Angle="90"/>
</Rectangle.LayoutTransform>
</Rectangle>
</DataTemplate>
</ComboBox.ItemTemplate>
<ComboBox.Items>
<!-- Simulates a Linear Interpolated color map -->
<s3D:GradientColorPalette IsStepped="False">
<s3D:GradientColorPalette.GradientStops>
<GradientStop Offset="1" Color="DarkRed"/>
<GradientStop Offset="0.9" Color="Red"/>
<GradientStop Offset="0.7" Color="Yellow"/>
<GradientStop Offset="0.5" Color="GreenYellow"/>
<GradientStop Offset="0.3" Color="Cyan"/>
<GradientStop Offset="0.1" Color="Blue"/>
<GradientStop Offset="0.0" Color="#1D2C6B"/>
</s3D:GradientColorPalette.GradientStops>
</s3D:GradientColorPalette>
<!-- Simulates hard steps between colors -->
<s3D:GradientColorPalette IsStepped="True">
<s3D:GradientColorPalette.GradientStops>
<GradientStop Offset="1" Color="DarkRed"/>
<GradientStop Offset="0.9" Color="Red"/>
<GradientStop Offset="0.7" Color="Yellow"/>
<GradientStop Offset="0.5" Color="GreenYellow"/>
<GradientStop Offset="0.3" Color="Cyan"/>
<GradientStop Offset="0.1" Color="Blue"/>
<GradientStop Offset="0.0" Color="#1D2C6B"/>
</s3D:GradientColorPalette.GradientStops>
</s3D:GradientColorPalette>
<!-- Simulates any brush e.g. an image, which can be turned into a mesh color map -->
<s3D:BrushColorPalette>
<s3D:BrushColorPalette.Brush>
<VisualBrush>
<VisualBrush.Visual>
<Canvas Width="512" Height="512" Background="Yellow" Grid.Row="1" HorizontalAlignment="Left">
<Ellipse Fill="Black" Width="75" Height="75" Canvas.Left="125" Canvas.Top="150"/>
<Ellipse Fill="Black" Width="75" Height="75" Canvas.Left="325" Canvas.Top="150"/>
<Path Stroke="Black" Data="M 125,300 S 225,400 325, 300" StrokeThickness="20" StrokeEndLineCap="Round" StrokeStartLineCap="Round" Canvas.Left="35"/>
</Canvas>
</VisualBrush.Visual>
</VisualBrush>
</s3D:BrushColorPalette.Brush>
</s3D:BrushColorPalette>
</ComboBox.Items>
</ComboBox>
<TextBlock Text="Mesh Palette" FontSize="12"/>
<ComboBox x:Name="MeshPaletteModeCombo" SelectedIndex="0" SelectionChanged="MeshPaletteModeCombo_OnSelectionChanged">
<ComboBox.Items>
<s3D:MeshPaletteMode>HeightMapSolidCells</s3D:MeshPaletteMode>
<s3D:MeshPaletteMode>HeightMapInterpolated</s3D:MeshPaletteMode>
<s3D:MeshPaletteMode>Textured</s3D:MeshPaletteMode>
<s3D:MeshPaletteMode>TexturedSolidCells</s3D:MeshPaletteMode>
</ComboBox.Items>
</ComboBox>
</StackPanel>
</ext:FlyoutMenuButton.PopupContent>
</ext:FlyoutMenuButton>
<ext:FlyoutMenuButton Style="{StaticResource FlyoutMenuButtonStyle}" Content="DATA" FontSize="9" Padding="0">
<ext:FlyoutMenuButton.PopupContent>
<StackPanel Orientation="Vertical" MinWidth="150">
<TextBlock Text="Choose what data to Render" FontSize="12"/>
<ComboBox x:Name="DataCombo" SelectedIndex="2" SelectionChanged="DataCombo_OnSelectionChanged">
<ComboBox.Items>
<system:String>3D Sinc 10 x 10</system:String>
<system:String>3D Sinc 50 x 50</system:String>
<system:String>3D Sinc 100 x 100</system:String>
<system:String>3D Sinc 500 x 500</system:String>
<system:String>3D Sinc 1k x 1k</system:String>
</ComboBox.Items>
</ComboBox>
</StackPanel>
</ext:FlyoutMenuButton.PopupContent>
</ext:FlyoutMenuButton>
</ext:SciChart3DInteractionToolbar>
<s3D:SciChart3DSurface x:Name="SciChart3DSurface" Grid.Column="1" WorldDimensions="200,100,200" BorderThickness="0"
RenderPriority="Normal" MaxFrameRate="36">
<s3D:SciChart3DSurface.Camera>
<s3D:Camera3D ZoomToFitOnAttach="True"/>
</s3D:SciChart3DSurface.Camera>
<s3D:SciChart3DSurface.RenderableSeries>
<s3D:SurfaceMeshRenderableSeries3D x:Name="SurfaceMesh"
Opacity="{Binding Source={x:Reference opacitySlider}, Path=Value, Mode=TwoWay}"
CellHardnessFactor="{Binding Source={x:Reference Name=normalHardnessSlider}, Path=Value, Mode=TwoWay}"
Shininess="{Binding Source={x:Reference Name=shininessSlider}, Path=Value, Mode=TwoWay, Converter={StaticResource LinearToLogarithmicValueConverter}}"
MeshColorPalette="{Binding Source={x:Reference Name=ColorMapCombo}, Path=SelectedItem, FallbackValue={StaticResource DefaultMeshColorPalette}}"
StrokeThickness="2"
Stroke="#7FFFFFFF"
DrawSkirt="True"
Minimum="0.0"
Maximum="0.5"
MeshPaletteMode="{Binding Source={x:Reference Name=MeshPaletteModeCombo}, Path=SelectedItem}"
DrawMeshAs="{Binding Source={x:Reference Name=DrawMeshAsCombo}, Path=SelectedItem}"
Style="{StaticResource SurfaceMeshStyle}"/>
</s3D:SciChart3DSurface.RenderableSeries>
<s3D:SciChart3DSurface.XAxis>
<s3D:NumericAxis3D AutoRange="Always"/>
</s3D:SciChart3DSurface.XAxis>
<s3D:SciChart3DSurface.YAxis>
<s3D:NumericAxis3D VisibleRange="0,1"/>
</s3D:SciChart3DSurface.YAxis>
<s3D:SciChart3DSurface.ZAxis>
<s3D:NumericAxis3D AutoRange="Always"/>
</s3D:SciChart3DSurface.ZAxis>
</s3D:SciChart3DSurface>
<s:HeatmapColorMap Margin="15" Grid.Column="1"
HorizontalAlignment="Right"
VerticalAlignment="Stretch"
Background="{Binding Source={x:Reference Name=SciChart3DSurface}, Path=Background}"
Foreground="{Binding Source={x:Reference Name=SciChart3DSurface}, Path=Foreground}"
ColorMap="{Binding Source={x:Reference Name=ColorMapCombo}, Path=SelectedItem.Brush}"
Minimum="{Binding Source={x:Reference Name=SurfaceMesh}, Path=Minimum}"
Maximum="{Binding Source={x:Reference Name=SurfaceMesh}, Path=Maximum}"
TextFormatting="0.00"
Opacity="0.8"
BorderBrush="#777"
BorderThickness="1"
Orientation="Vertical">
<s:HeatmapColorMap.Style>
<Style TargetType="s:HeatmapColorMap">
<Style.Triggers>
<DataTrigger Binding="{Binding Source={x:Reference Name=ColorMapCombo}, Path=SelectedIndex}" Value="2">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</s:HeatmapColorMap.Style>
</s:HeatmapColorMap>
</Grid>
</UserControl>CreateRealTime3DUniformMeshChart.xaml.cs
View source code// *************************************************************************************
// SCICHART® Copyright SciChart Ltd. 2011-2022. All rights reserved.
//
// Web: http://www.scichart.com
// Support: support@scichart.com
// Sales: sales@scichart.com
//
// CreateRealTime3DUniformMeshChart.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;
using System.Threading.Tasks;
using System.Timers;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using SciChart.Charting3D.Model;
using SciChart.Charting3D.RenderableSeries;
using SciChart.Data.Model;
namespace SciChart.Examples.Examples.Charts3D.CreateRealtime3DCharts
{
/// <summary>
/// Interaction logic for CreateRealTime3DSurfaceMeshChart.xaml
/// </summary>
public partial class CreateRealTime3DSurfaceMeshChart : UserControl
{
private Timer _timer;
private bool _processingUpdate;
private object _syncRoot = new object();
public CreateRealTime3DSurfaceMeshChart()
{
InitializeComponent();
this.Loaded += (s, e) =>
{
this.StartButton.IsChecked = true;
this.OnStart();
};
this.Unloaded += (s, e) =>
{
this.OnStop();
};
}
private void StartButton_Click(object sender, RoutedEventArgs e)
{
OnStart();
}
private void PauseButton_Click(object sender, RoutedEventArgs e)
{
OnStop();
}
private void DataCombo_OnSelectionChanged(object sender, EventArgs e)
{
OnStart();
}
private void OnStart()
{
if (!IsLoaded) return;
string whatData = (string)DataCombo.SelectedItem;
int w = 0, h = 0;
if (whatData == "3D Sinc 10 x 10") w = h = 10;
if (whatData == "3D Sinc 50 x 50") w = h = 50;
if (whatData == "3D Sinc 100 x 100") w = h = 100;
if (whatData == "3D Sinc 500 x 500") w = h = 500;
if (whatData == "3D Sinc 1k x 1k") w = h = 1000;
lock (_syncRoot)
{
OnStop();
}
var dataSeries = new UniformGridDataSeries3D<double>(w, h)
{
StartX = 0,
StartZ = 0,
StepX = 10 / (w - 1d),
StepZ = 10 / (h - 1d),
SeriesName = "Realtime Surface Mesh",
};
var frontBuffer = dataSeries.InternalArray;
var backBuffer = new GridData<double>(w, h).InternalArray;
int frames = 0;
_timer = new Timer();
_timer.Interval = 20;
_timer.Elapsed += (s, arg) =>
{
lock (_syncRoot)
{
double wc = w*0.5, hc = h*0.5;
double freq = Math.Sin(frames++*0.1)*0.1 + 0.1;
// Each set of dataSeries[i,j] schedules a redraw when the next Render event fires. Therefore, we suspend updates so that we can update the chart once
// Data generation (Sin, Sqrt below) is expensive. We parallelize it by using Parallel.For for the outer loop
// Equivalent of "for (int j = 0; j < h; j++)"
// This will result in more CPU usage, but we wish to demo the performance of the actual rendering, not the slowness of generating test data! :)
Parallel.For(0, h, i =>
{
var buf = frontBuffer;
for (int j = 0; j < w; j++)
{
// 3D Sinc function from http://www.mathworks.com/matlabcentral/cody/problems/1305-creation-of-2d-sinc-surface
// sin(pi*R*freq)/(pi*R*freq)
// R is distance from centre
double radius = Math.Sqrt((wc - i)*(wc - i) + (hc - j)*(hc - j));
var d = Math.PI*radius*freq;
var value = Math.Sin(d)/d;
buf[i][j] = double.IsNaN(value) ? 1.0 : value;
}
});
using (dataSeries.SuspendUpdates(false, true))
{
dataSeries.CopyFrom(frontBuffer);
var temp = backBuffer;
backBuffer = frontBuffer;
frontBuffer = temp;
}
}
};
SurfaceMesh.DataSeries = dataSeries;
_timer.Start();
StartButton.IsEnabled = false;
PauseButton.IsEnabled = true;
}
private void OnStop()
{
if (_timer != null)
{
_timer.Stop();
StartButton.IsEnabled = true;
PauseButton.IsEnabled = false;
}
}
private void ColorMapCombo_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (!IsLoaded) return;
// Valid combinations check. If palette is a LinearGradientBrush and we are in texture mode, switch to heightmap mode
if (((BrushColorPalette)ColorMapCombo.SelectedItem).Brush is LinearGradientBrush &&
(MeshPaletteModeCombo.SelectedItem.Equals(MeshPaletteMode.Textured) || MeshPaletteModeCombo.SelectedItem.Equals(MeshPaletteMode.TexturedSolidCells)))
{
MeshPaletteModeCombo.SelectedItem = MeshPaletteMode.HeightMapInterpolated;
}
// Valid combinations check. If palette is a TextureBrush and we are not in texture mode, switch to texture mode
else if (((BrushColorPalette)ColorMapCombo.SelectedItem).Brush is VisualBrush &&
(MeshPaletteModeCombo.SelectedItem.Equals(MeshPaletteMode.HeightMapInterpolated) || MeshPaletteModeCombo.SelectedItem.Equals(MeshPaletteMode.HeightMapSolidCells)))
{
MeshPaletteModeCombo.SelectedItem = MeshPaletteMode.Textured;
}
}
private void MeshPaletteModeCombo_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (!IsLoaded) return;
// Valid combinations check. If mesh palette is a heightmap, then choose a gradient brush for the palette
if ((MeshPaletteModeCombo.SelectedItem.Equals(MeshPaletteMode.HeightMapInterpolated) || MeshPaletteModeCombo.SelectedItem.Equals(MeshPaletteMode.HeightMapSolidCells))
&& ((BrushColorPalette)ColorMapCombo.SelectedItem).Brush is VisualBrush)
{
ColorMapCombo.SelectedIndex = 0;
}
// Valid combinations check. If mesh palette is textured, then choose a texture brush for the palette
else if (MeshPaletteModeCombo.SelectedItem.Equals(MeshPaletteMode.Textured) ||
MeshPaletteModeCombo.SelectedItem.Equals(MeshPaletteMode.TexturedSolidCells))
{
ColorMapCombo.SelectedIndex = ColorMapCombo.Items.Count - 1;
}
}
}
}
Back to WPF Chart Examples


