LightningChart .NETTables in WPF

TutorialImplementation of tables in WPF with LightningChart .NET

Written by a human | Updated on April 23rd, 2025

Tables in WPF

In this exercise, we’ll create a chart with data annotations. This is a great opportunity to explore the Point Line Series with two Y axes.

On the other hand, data annotations are another fantastic feature of Lightning Chart .NET. With them, we can create a data table right on the chart, giving users the ability to interact with some visual properties of this table. The annotation table can display multiple values and is easily created using C# code, utilizing For loops and other programming tools.

Working with annotations is similar to working with HTML tables. Essentially, you create a parent AnnotationXY object and then add nested AnnotationXY objects as cells. Configuring visual properties like font, color, and borders is much like assigning CSS properties.

If you’ve worked with data annotations in LC JS, this example will feel very familiar. There are plenty of articles that cover point and line series, so I encourage you to check them out as well. This article, however, will focus on setting up data annotations.

As with other .NET articles, I’ve included a brief introduction to WPF. If you’re not familiar with this framework, I recommend reading through that introduction. Also, be sure to review the technical requirements and the explanation for installing the LC .NET framework along with the interactive examples tool.

What is LightningChart’s WPF graph?

LightningChart’s WPF graph, known as LightningChart for WPF, is a powerful data visualization tool designed specifically for Microsoft Windows Presentation Foundation (WPF). WPF charts can easily be integrated into your Microsoft Visual Studio toolbox.

You can add them to your software project using drag-and-drop, XAML, or code-behind. Once added, the WPF graph becomes a key part of the user interface (UI) in your Windows desktop application, making it an invaluable tool for software developers.

What is WPF?

Windows Presentation Foundation (WPF) is a framework designed for creating applications with sophisticated user interfaces. It consists of two main components: Markup, where you define the structure and appearance, and code-behind, where you manage the application’s logic and behavior.

Project Overview

Today we will create tables in WPF using LightningChart .NET charting components.

tables-in-wpf-annotations-chart

zip icon
Download the project to follow the tutorial

Markup

Markup enables us to build a user interface with a variety of controls, allowing us to manage the displayed results in the application with great precision. This interface development is done using XAML (Extensible Application Markup Language). While it might initially look like an XML template, XAML is specifically designed for building application interfaces rather than just exchanging data between applications.

The interface’s data, graphics, and animations can either be pulled from an external source file or dynamically generated through the code behind.

<Window
	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
	Title="Window with Button"
	Width="250" Height="100">

  <!-- Add button to window -->
	<Button Name="button">Click Me!</Button>

</Window>

The creation of some controls can be almost the same as that of HTML tags, in other cases, properties such as style is limited to the declaration of a property within a tag.

Code Behind

The code behind refers to the file containing the executable code responsible for reading, generating, and processing the results the user needs. One of its main purposes is to separate the graphical interface code (like XAML, HTML, CSS, etc.) from the executable code. This separation allows us to divide the work between user interface design and the development of the underlying code, leading to safer, more organized, and faster development.

In the case of WPF (Windows Presentation Foundation), we use the C# programming language. C# is an object/component-oriented language that fits well with this approach. Lightning Chart .NET generates WPF projects with C# code that’s ready for execution. Within this code, you can use LightningChart .NET own tools, which can be easily imported if the LC .NET framework is installed.

Local Setup

For this project, we need to consider the following requirements to compile the project.

  1. OS: 32-bit or 64-bit Windows Vista or later, Windows Server 2008 R2 or later.
  2. DirectX: 9.0c (Shader model 3 and higher) or 11.0 compatible graphics adapter.
  3. Visual Studio: 2010-2019 for development, not required for deployment.
  4. Platform .NET Framework: installed version 4.0 or newer.

Now go to the next URL and download LightningChart .NET. You’ll then be redirected to a sign-in form where you’ll have to complete a simple sign-up process to get access to your LightningChart account.

Example-LightningChart-Account

After signing in to your account, you can download the SDK “free trial” version that allows you to use important features for this tables in WPF tutorial. When you download the SDK, you’ll have a .exe file like this:

LightningChart-exe-installation

The installation will be a typical Windows process, so please continue with it until it is finished. After the installation, you will see the following programs:

LightningChart-.NET-Installed-Programs

License Manager

In this application, you will see the purchase options. All the projects that you will create with this trial SDK, will be available for future developments with all features enabled.

Purchase-Options-LightningChart-.NET

LightningChart .NET Interactive Examples

You can see 100+ interactive visualizations for WPF, WinForms, and/or UWP.

LightningChart-.NET-Interactive-Examples

Visual Studio Project

The main difference between the LightningChart visualizer and Visual Studio is that we can analyze and experiment with many features within the source code. In the LC visualizer, select the “Annotations table chart” example and run it:

wpf-scatter-chart-visual-studio-project

In the top-right zone of the windows, you will see the following options:

Project-Options-LightningChart-.NET_

The SDK trial allows us to use the WPF framework. After selecting the right framework, we need to specify a folder where to create the tables in WPF chart project:

tables-in-wpf-folder

After creating the project, Visual Studio will open and be ready for execution.

wpf-3d-sphere-Visual-Studio-Project-Ready

XAML Code Review

The main XAML code will be wrapped inside MainWindow.xaml.cs and contains the code of the sphere GUI controls.

UI-controls-of-LightningChart-.NET

Inside the code, we will check two methods for creating the properties needed to draw the tables in WPF correctly. The interactive example is built with various user controls to manipulate and change the visual properties of the chart. These controls are not required to generate this graph, so we will focus on the code responsible for generating the object.

CreateChart()

This method will be responsible for configuring our chart, adding the axes, groups, colors, etc. We will create our chart:

// Create a new chart.
_chart = new LightningChart();

_chart.BeginUpdate();

The BeginUpdate function will allow us to stop drawing the chart, which will allow us to set up the properties that we want to customize. Now we will assign a title to our tables in WPF chart:

_chart.ViewXY.DataCursor.Visible = true;
//Chart name
_chart.ChartName = "Annotations table chart";

The DataCursor property (true), displays a small data table with the values ​​at the point where the cursor is pointing.

tables-in-wpf-datacursor-property

If the value is false, this data table won’t be displayed. If we want, we can display a legend box in our chart. If the value is false, the box will be hidden.

//Hide legend box
_chart.ViewXY.LegendBoxes[0].Visible = false;

Axes Setup

To access the X-axis, we just need to select index zero from the XAxes list. Since we are only using a chart with a single X axis, it will be assigned by default to index zero:

// Configure x-axis.
AxisX xAxis = _chart.ViewXY.XAxes[0];
xAxis.SetRange(0, 100);
xAxis.ScrollMode = XAxisScrollMode.None;
xAxis.ValueType = AxisValueType.Number;

The range will be from 0 to 100:

tables-in-wpf-axes-range

The scroll mode offers us 5 behavior options for scrolling the X-axis

  • None: The X-axis is not automatically scrolled.
  • Scrolling: The X-axis will be scrolled when the scroll position approaches the end of the graph.
  • Stepping: Steps the view with Step when the scroll position reaches the end of the graph.
  • Sweeping: Previous traces will be left in the background and new traces will be swept over.
  • Triggering: The X-axis minimum and maximum are set by the detected level trigging position.
_chart.ViewXY.YAxes.Clear(); // Remove existing y-axes.

AxisY yAxisTemperature = new AxisY(_chart.ViewXY);
yAxisTemperature.Title.Text = "Temperature, °C";
yAxisTemperature.SetRange(-30, 50);
yAxisTemperature.AxisColor = TemperatureColor;
yAxisTemperature.Title.Color = TemperatureColor;
_chart.ViewXY.YAxes.Add(yAxisTemperature);

We can clear the X or Y axis using the function Clear. This is very helpful when we need to update the axes ranges or visual properties. The colors were specified at the beginning of the code.

private static Color TemperatureColor = Colors.DarkOrange;
private static Color HumidityColor = Colors.Red;

We can have more than one axis. In this case, we are creating another Y axis, that will be displayed at the right side of the chart. This axis will indicate the Humidity percentage.

AxisY yAxisHumidity = new AxisY(_chart.ViewXY);
yAxisHumidity.Title.Text = "Humidity, %";
yAxisHumidity.SetRange(0, 100);
yAxisHumidity.MajorGrid.Visible = false;
yAxisHumidity.MinorGrid.Visible = false;
yAxisHumidity.AxisColor = HumidityColor;
yAxisHumidity.Title.Color = HumidityColor;
_chart.ViewXY.YAxes.Add(yAxisHumidity);

The setup of this second axis is the same as the previous one. The horizontal grid lines are drawn on the vertical positions of division ticks. Major grid for major division ticks, minor grid for minor division ticks. Use MajorGrid and MinorGrid properties to edit the appearance.

_chart.ViewXY.AxisLayout.YAxisAutoPlacement = YAxisAutoPlacement.LeftThenRight;

YAxisAutoPlacement = LeftThenRight. Axes are distributed to the left and right sides of the graph, every other axis to the opposite side, starting from the left side.

Series Setup

We will create 5 series

string[] cityNames = new string[] { "Paris", "New York City", "Tokyo", "Helsinki", "Buenos Aires" };
Color[] cityColors = new Color[] { Colors.LimeGreen, Colors.LightCoral, Colors.RoyalBlue, Colors.Green, Colors.Turquoise };

The city names and city colors will be stored in arrays objects, because these will be mapped in a loop:

for (int i = 0; i < _dataRowCount; i++)
{
    PointLineSeries temperatureSeries = new PointLineSeries(_chart.ViewXY, _chart.ViewXY.XAxes[0], yAxisTemperature);
    temperatureSeries.LineStyle.Color = cityColors[i];
    temperatureSeries.PointsVisible = false;
    temperatureSeries.PointStyle.Color1 = Color.FromArgb(0, 0, 0, 0);
    temperatureSeries.PointStyle.Color2 = cityColors[i];
    _chart.ViewXY.PointLineSeries.Add(temperatureSeries);
    temperatureSeries.Points = CreateRandomTemperatureData(101, i * 5);

A point line series is basically a representation of each data point in the cartesian plane. These points will be located with their X and Y values, in relation to the X and Y axes. These points can be joined by lines, in this way, we obtain as a result, a line with peaks:

tables-in-wpf-pointLineSeries

The data should be formatted as an array, this array will be created with the methods CreateRandomTemperatureData and CreateRandomHumidityData.

PointLineSeries humiditySeries = new PointLineSeries(_chart.ViewXY, _chart.ViewXY.XAxes[0], yAxisHumidity);
humiditySeries.LineStyle.Color = cityColors[i];
humiditySeries.PointsVisible = true;
humiditySeries.PointStyle.Color1 = HumidityColor;
humiditySeries.PointStyle.Color2 = Color.FromArgb(0, 0, 0, 0);
humiditySeries.PointStyle.Shape = Shape.Rectangle;
humiditySeries.PointStyle.Angle = 45;
_chart.ViewXY.PointLineSeries.Add(humiditySeries);
humiditySeries.Points = CreateRandomHumidityData(101, i * 10);

The main difference between the first- and second-point line series, is that we are using the Y temperature axis for the temperature series and Y humidity for the humidity series.

Creating Data Points

private SeriesPoint[] CreateRandomTemperatureData(int pointCount, double seedOffsetLevel)
{
    Random random = new Random((int)seedOffsetLevel);

    double value = seedOffsetLevel;

    SeriesPoint[] data = new SeriesPoint[pointCount];

    for (int i = 0; i < pointCount; i++)
    {
        data[i].X = i;
        value += (random.NextDouble() - 0.5) * 3;

        data[i].Y = value;
    }
    return data;
}

For this example, we are using a simple loop to create random data points. As you can see, the data points only need the X and Y values. These points must be contained within the range of each axis.

CreateAnnotationsTable()

This is the method to create the tables in WPF. It will create and display the annotation table over the chart:

tables-in-wpf-annotations-table

The parameters are the cityNames, cityColors, width, and height.

private void CreateAnnotationsTable(string[] cityNames, Color[] cityColors, float width, float height)
{

With these parameters, we will specify the size of the table, the number and color of rows. To create the resizing border, we need to allow the interaction with the user, allow resize function and other visual properties:

AnnotationXY resizingBorder = CreateCell();
resizingBorder.AllowUserInteraction = true;
resizingBorder.AllowResize = true;
resizingBorder.AllowDragging = false;
resizingBorder.Fill.Style = RectFillStyle.None;
resizingBorder.Shadow.Visible = false;
resizingBorder.ResizedByUser += resizingBorder_ResizedByMouse;
resizingBorder.TextStyle.Visible = false;
resizingBorder.SizeScreenCoords.SetValues(width, height);
resizingBorder.Highlight = Highlight.None;
resizingBorder.BorderLineStyle.Width = 3;

SizeScreenCoords will help us to set the default size of the table. ResizedByUser will update the size of the table according to the user:

private void resizingBorder_ResizedByMouse(object sender, AnnotationResizedByUserXYEventArgs e)
{
    AnnotationXY border = (AnnotationXY)sender;
    RepositionAnnotationsTable(border.LocationScreenCoords.X, border.LocationScreenCoords.Y,
        border.SizeScreenCoords.Width, border.SizeScreenCoords.Height);
}

All these properties will be stored in the AnnotationXY object. This object will contain the border, header, rows, and columns. The headers are cells, and we just need to add them in the order that we need to:

tables-in-wpf-annotation-xy
AnnotationXY header = CreateCell();
header.TextStyle.Font = new WpfFont("Serif", 20.0, "20", true, false);
header.TextStyle.Color = Colors.White;
header.TextStyle.Shadow.Style = TextShadowStyle.DropShadow;

header.Text = "Weather data";
header.Fill.Style = RectFillStyle.ColorOnly;
header.Fill.Color = Color.FromArgb(180, 255, 255, 0);
header.Fill.GradientColor = Color.FromArgb(180, 200, 200, 0);
header.Fill.GradientFill = GradientFill.Linear;
header.AllowUserInteraction = true;
header.AllowDragging = true;
header.DraggedByUser += header_MovedByMouse;

//Column titles - City 
AnnotationXY columnTitle1 = CreateCell();
columnTitle1.TextStyle.Font = new WpfFont("Serif", 15.0, "15", true, false);
columnTitle1.TextStyle.Color = Colors.White;
columnTitle1.Text = "City";
columnTitle1.Fill.Style = RectFillStyle.ColorOnly;
columnTitle1.Fill.Color = Colors.Gray;
columnTitle1.TextStyle.HorizAlign = AlignmentHorizontal.Left;

All the properties are very similar to CSS properties, so I think is not too necessary to explain all of them. The CreateCell function will add more properties to the cell. The color of the cells will be taken from the axis.

To create the rows, we need to create more cells using a loop:

for (int row = 0; row < cityCount; row++)
{
    //Row title 
    AnnotationXY cityTitle = CreateCell();
    cityTitle.TextStyle.Font = new WpfFont("Serif", 12.0, "12", true, true);
    cityTitle.TextStyle.Color = Colors.White;
    cityTitle.Text = cityNames[row];
    cityTitle.Fill.Color = cityColors[row];
    cityTitle.Fill.Style = RectFillStyle.ColorOnly;
    cityTitle.TextStyle.HorizAlign = AlignmentHorizontal.Left;

    //Data cells 
    for (int dataCol = 0; dataCol < dataColumnsPerCity; dataCol++)
    {
        AnnotationXY data = CreateCell();
        data.TextStyle.Font = new WpfFont("Serif", 12.0, "12", false, false);
        data.TextStyle.Color = Colors.White;
        data.Text = "N/A";
    }
}
For each city, a cell will be created. The data columnsPerCity was specified at the beginning of the code.
tables-in-wpf-columns-per-city

We specified the value two and we will have two columns by city:

tables-in-wpf-columns

Adding data to the table

The table will need an object with the two values for each column. In this case, we will use tuples. The tuple helps to have a data structure that consists in multiple parts by “row”:

Tuple<double, double>[] data = new Tuple<double, double>[5];
data[0] = new Tuple<double, double>(8, 24);
data[1] = new Tuple<double, double>(10, 45);
data[2] = new Tuple<double, double>(20, 12);
data[3] = new Tuple<double, double>(-15, 5);
data[4] = new Tuple<double, double>(40, 68);
UpdateTableData(data);
tables-in-wpf-columns

The method UpdateTableData will help us to add each value to the AnnotationXY object. In this way, you can implement a way to create a tuple with updated values.

_chart.BeginUpdate();

for (int iCity = 0; iCity < _dataRowCount; iCity++)
{
    double tempature = data[iCity].Item1;
    AnnotationXY cellTemperature = _chart.ViewXY.Annotations[5 + iCity * 3 + 1];
    cellTemperature.Text = tempature.ToString() + " °C";
    if (tempature < 0)
    {
        cellTemperature.TextStyle.Color = Colors.LightBlue;
    }
    else if (tempature < 20)
    {
        cellTemperature.TextStyle.Color = Colors.White;
    }
    else
    {
        cellTemperature.TextStyle.Color = Colors.Red;
    }

    AnnotationXY cellHumidity = _chart.ViewXY.Annotations[5 + iCity * 3 + 2];
    cellHumidity.Text = data[iCity].Item2.ToString() + " %";
}

_chart.EndUpdate();

The method will format the values, giving a color related to the temperature. The way to locate the corresponding cell, is basically counting the number of cells and adding the city row.

_chart.EndUpdate();

gridChart.Children.Add(_chart);

Once we have finished configuring our chart, we close the EndUpdate() update process, and add the chart object to our XAML grid so that it is displayed.

Conclusion

Annotations might seem a bit confusing at first, especially with all the code needed to configure their visual properties. But here’s the good news: LightningChart .NET allows you to customize these annotations in a way that’s quite similar to styling with CSS classes.

When working with tables in WPF, you’ll find that there’s a wide range of properties available. You can explore these in the official LC .NET documentation or by diving into the classes, enumerators, and interfaces provided by the library.

One thing that might trip you up is how tables are created—but don’t worry, it’s easier than it looks! Think of an annotation as a box or cell, kind of like a “DIV” in CSS and HTML. Inside this cell, you can add the text and information you need.

To create tables in WPF, you simply need to make a set of cells, all neatly arranged within a frame that keeps your content organized. Once your table is set up, you just assign values to it using the text property. You can customize it with fill colors, borders, text, font type, font size, and more.

The resize and table update functions work like general functions that you can apply to any .NET project. So, if you’re looking to implement annotations or tables in WPF for your projects, this exercise will give you the tools you need to focus on the data source.

Even when it comes to creating line series, you can take this code and use it in other projects. The logic remains the same; only the data range, colors, and labels would change.

I hope this exercise has been helpful for your upcoming projects. Take care!

Omar Urbano Software Engineer

Omar Urbano

Software Engineer

LinkedIn icon
divider-light

Continue learning with LightningChart

How to Create a Strip Chart

How to Create a Strip Chart

Written by a human | Updated on April 9th, 2025What is a Strip chart application and what are the modern equivalents to it?  Before computers exist or were taking their first steps, a Strip chart was a way to visualize an analog electrical signal. Voltage was...

Data Visualization Template for Electron JS | LightningChart®

Updated on April 4th, 2025 | Written by humanAre you already building cross-platform applications with Electron JS?  In some of our previous articles, we’ve worked on TypeScript projects where we created pie charts and vibration chart applications. And as we...

Bar chart race JavaScript

Bar chart race JavaScript

Updated on April 14th, 2025 | Written by humanBar chart race JavaScript  When I wrote this article, the COVID-19 pandemic was at its peak point. Today, things are much better thanks to vaccinations that continued their steady positive global effect. With this bar...