diff --git a/README.md b/README.md
index 469e157..954e3f8 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,233 @@
-# How to add multiple trackballs in a WPF SfChart.
\ No newline at end of file
+# How to add multiple trackballs in a WPF SfChart.
+This article provides a detailed walkthrough on how to add multiple trackballs in an [SfChart](https://help.syncfusion.com/wpf/charts/getting-started) in WPF, allowing you to hover over the trackball with your mouse and move them independently to view the information of different data points simultaneously.
+
+Learn step-by-step instructions and gain insights to add multiple trackballs in a WPF SfChart.
+
+**Step 1:** Initialize the SfChart with primary and secondary axes. For more detailed steps, refer to the WPF Charts [documentation](https://help.syncfusion.com/wpf/charts/getting-started).
+
+XAML
+
+```XML
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+**Step 2:** Create a custom ChartTrackBallBehaviorExt class, which is inherited from [ChartTrackballBehavior](https://help.syncfusion.com/cr/wpf/Syncfusion.UI.Xaml.Charts.ChartTrackBallBehavior.html#).
+
+C#
+
+```csharp
+public class ChartTrackBallBehaviorExt : ChartTrackBallBehavior
+{
+
+}
+```
+
+**Step 3:** Create instances of ChartTrackBallBehaviorExt, and add them to the [Behaviors](https://help.syncfusion.com/cr/wpf/Syncfusion.UI.Xaml.Charts.SfChart.html#Syncfusion_UI_Xaml_Charts_SfChart_Behaviors) collection, assigning specific names to each.
+
+XAML
+
+```XML
+
+
+
+
+```
+
+**Step 4:** Implement the **ChartTrackBallBehaviorExt** class and its functionalities. Include the **Display** method, which is responsible for displaying the trackball at specified coordinates by setting the IsActivated protected property of the ChartTrackBallBehavior class. Manage multiple trackballs by overriding **mouse event handlers** in ChartTrackBallBehavior, using the **FindNearestTrackball** method in **OnMouseEnter** to locate the closest trackball. The **isTrackballActive** variable ensures only the active trackball responds to the events.
+
+C#
+
+```csharp
+public class ChartTrackBallBehaviorExt : ChartTrackBallBehavior
+{
+ private bool isTrackballActive = false;
+
+ public SfChart? SfChart { get; set; }
+
+ public double X { get; set; }
+ public double Y { get; set; }
+
+ protected override void OnMouseEnter(MouseEventArgs e)
+ {
+ // Get the position of the mouse pointer
+ var touchPoint = e.GetPosition(null);
+
+ // Find the nearest trackball to the mouse pointer
+ var trackball = FindNearestTrackball(touchPoint);
+
+ // Activate the trackball if it is the nearest one
+ if (trackball == this)
+ {
+ isTrackballActive = true;
+ base.OnMouseEnter(e);
+ }
+ }
+
+ protected override void OnMouseMove(MouseEventArgs e)
+ {
+ // Check if the trackball is activated
+ if (isTrackballActive)
+ {
+ // Get the position of the mouse pointer
+ var touchPoint = e.GetPosition(null);
+
+ // Display the trackball at the current mouse position
+ Display((float)touchPoint.X, (float)touchPoint.Y);
+ base.OnMouseMove(e);
+ }
+ }
+
+ protected override void OnMouseLeave(MouseEventArgs e)
+ {
+ // Deactivate the trackball
+ isTrackballActive = false;
+ }
+
+ private ChartTrackBallBehavior FindNearestTrackball(Point touchPoint)
+ {
+ ChartTrackBallBehavior nearestTrackball = new ChartTrackBallBehaviorExt();
+ double minDistance = double.MaxValue;
+
+ // Iterate through all trackball behaviors to find the nearest one
+ if (SfChart != null)
+ {
+ foreach (var trackballBehaviour in SfChart.Behaviors)
+ {
+ if (trackballBehaviour is ChartTrackBallBehaviorExt trackball)
+ {
+ // Calculate the distance between the trackball and the touch point
+ double distance = Math.Sqrt(Math.Pow(trackball.X - touchPoint.X, 2) + Math.Pow(trackball.Y - touchPoint.Y, 2));
+
+ // Update the nearest trackball if the current one is closer
+ if (distance < minDistance)
+ {
+ minDistance = distance;
+ nearestTrackball = trackball;
+ }
+ }
+ }
+ }
+ return nearestTrackball;
+ }
+
+ public void Display(float x, float y)
+ {
+ X = x; Y = y;
+ IsActivated = true;
+ var point = new Point(x, y);
+
+ // Set the internal property for the current point
+ SetInternalProperty(typeof(ChartTrackBallBehavior), this, point, "CurrentPoint");
+
+ // Trigger the pointer position changed event
+ base.OnPointerPositionChanged();
+
+ // Activate the trackball
+ InvokeInternalMethod(typeof(ChartTrackBallBehavior), this, "Activate", IsActivated);
+ }
+
+ // Sets an internal property of an object using reflection.
+ internal static void SetInternalProperty(Type type, object obj, object value, string propertyName)
+ {
+ var properties = type.GetRuntimeProperties();
+
+ foreach (var item in properties)
+ {
+ if (item.Name == propertyName)
+ {
+ item.SetValue(obj, value);
+ break;
+ }
+ }
+ }
+
+ // Invokes an internal method of an object using reflection.
+ internal static object? InvokeInternalMethod(Type type, object obj, string methodName, params object[] args)
+ {
+ var method = type.GetTypeInfo().GetDeclaredMethod(methodName);
+ return method?.Invoke(obj, args);
+ }
+}
+```
+
+**Step 5:** Assign the chart instance to the **SfChart** property in the **ChartTrackBallBehaviorExt** class, and override the **OnContentRendered** method to run the asynchronous task that calls **ShowTrackball** method.
+
+The **ShowTrackballMethod** method calculates the positions and displays the trackballs on the chart.
+
+C#
+
+```csharp
+public partial class MainWindow : Window
+{
+ public MainWindow()
+ {
+ InitializeComponent();
+ trackball1.SfChart = this.chart;
+ trackball2.SfChart = this.chart;
+ }
+
+ protected override void OnContentRendered(EventArgs e)
+ {
+ base.OnContentRendered(e);
+
+ // Run the ShowTrackball method asynchronously
+ Task.Run(async () =>
+ {
+ await ShowTrackball();
+ });
+ }
+
+ async Task ShowTrackball()
+ {
+ // Wait for 1 second before executing the rest of the method
+ await Task.Delay(1000);
+ Application.Current.Dispatcher.Invoke(() =>
+ {
+ // Calculated positions for the first trackball
+ float xPosition = (float)chart.ValueToPoint(chart.PrimaryAxis, 1);
+ float yPosition = (float)chart.ValueToPoint(chart.SecondaryAxis, 169);
+
+ // Calculated positions for the second trackball
+ float xPosition1 = (float)chart.ValueToPoint(chart.PrimaryAxis, 6);
+ float yPosition1 = (float)chart.ValueToPoint(chart.SecondaryAxis, 170);
+
+ // Display the first trackball
+ trackball1.Display(xPosition, yPosition);
+
+ // Display the second trackball
+ trackball2.Display(xPosition1, yPosition1);
+ });
+ }
+}
+```
+
+**Step 6:** To control the trackballs, simply hover over them with your mouse. As you move the mouse within the chart area, the trackball will follow the cursor, allowing you to inspect different data points interactively.
+
+**Output:**
+
+
+
+## Troubleshooting
+### Path too long exception
+If you are facing a path too long exception when building this example project, close Visual Studio and rename the repository to short and build the project.
+
+For more details, refer to the KB on [How to add multiple trackballs in a WPF SfChart?](https://support.syncfusion.com/kb/article/17741/how-to-add-multiple-trackballs-in-a-wpf-sfchart).
diff --git a/SfChartMultipleTrackball/SfChartMultipleTrackball.sln b/SfChartMultipleTrackball/SfChartMultipleTrackball.sln
new file mode 100644
index 0000000..57c64ea
--- /dev/null
+++ b/SfChartMultipleTrackball/SfChartMultipleTrackball.sln
@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.11.35327.3
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SfChartMultipleTrackball", "SfChartMultipleTrackball\SfChartMultipleTrackball.csproj", "{E5B30519-D065-42FC-B89A-B435C07011D9}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {E5B30519-D065-42FC-B89A-B435C07011D9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E5B30519-D065-42FC-B89A-B435C07011D9}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E5B30519-D065-42FC-B89A-B435C07011D9}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E5B30519-D065-42FC-B89A-B435C07011D9}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {0CA07FB0-0F65-49F3-AFCC-11C28CE1D119}
+ EndGlobalSection
+EndGlobal
diff --git a/SfChartMultipleTrackball/SfChartMultipleTrackball/App.xaml b/SfChartMultipleTrackball/SfChartMultipleTrackball/App.xaml
new file mode 100644
index 0000000..52a5812
--- /dev/null
+++ b/SfChartMultipleTrackball/SfChartMultipleTrackball/App.xaml
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/SfChartMultipleTrackball/SfChartMultipleTrackball/App.xaml.cs b/SfChartMultipleTrackball/SfChartMultipleTrackball/App.xaml.cs
new file mode 100644
index 0000000..583a85e
--- /dev/null
+++ b/SfChartMultipleTrackball/SfChartMultipleTrackball/App.xaml.cs
@@ -0,0 +1,14 @@
+using System.Configuration;
+using System.Data;
+using System.Windows;
+
+namespace SfChartMultipleTrackball
+{
+ ///
+ /// Interaction logic for App.xaml
+ ///
+ public partial class App : Application
+ {
+ }
+
+}
diff --git a/SfChartMultipleTrackball/SfChartMultipleTrackball/AssemblyInfo.cs b/SfChartMultipleTrackball/SfChartMultipleTrackball/AssemblyInfo.cs
new file mode 100644
index 0000000..b0ec827
--- /dev/null
+++ b/SfChartMultipleTrackball/SfChartMultipleTrackball/AssemblyInfo.cs
@@ -0,0 +1,10 @@
+using System.Windows;
+
+[assembly: ThemeInfo(
+ ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
+ //(used if a resource is not found in the page,
+ // or application resource dictionaries)
+ ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
+ //(used if a resource is not found in the page,
+ // app, or any theme specific resource dictionaries)
+)]
diff --git a/SfChartMultipleTrackball/SfChartMultipleTrackball/MainWindow.xaml b/SfChartMultipleTrackball/SfChartMultipleTrackball/MainWindow.xaml
new file mode 100644
index 0000000..7826457
--- /dev/null
+++ b/SfChartMultipleTrackball/SfChartMultipleTrackball/MainWindow.xaml
@@ -0,0 +1,106 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/SfChartMultipleTrackball/SfChartMultipleTrackball/MainWindow.xaml.cs b/SfChartMultipleTrackball/SfChartMultipleTrackball/MainWindow.xaml.cs
new file mode 100644
index 0000000..9b61a67
--- /dev/null
+++ b/SfChartMultipleTrackball/SfChartMultipleTrackball/MainWindow.xaml.cs
@@ -0,0 +1,165 @@
+using Syncfusion.UI.Xaml.Charts;
+using System.Reflection;
+using System.Windows;
+using System.Windows.Input;
+
+namespace SfChartMultipleTrackball
+{
+ ///
+ /// Interaction logic for MainWindow.xaml
+ ///
+ public partial class MainWindow : Window
+ {
+ public MainWindow()
+ {
+ InitializeComponent();
+ trackball1.SfChart = this.chart;
+ trackball2.SfChart = this.chart;
+ }
+
+ protected override void OnContentRendered(EventArgs e)
+ {
+ base.OnContentRendered(e);
+
+ // Run the ShowTrackball method asynchronously
+ Task.Run(async () =>
+ {
+ await ShowTrackball();
+ });
+ }
+
+ async Task ShowTrackball()
+ {
+ // Wait for 1 second before executing the rest of the method
+ await Task.Delay(1000);
+ Application.Current.Dispatcher.Invoke(() =>
+ {
+ // Calculated positions for the first trackball
+ float xPosition = (float)chart.ValueToPoint(chart.PrimaryAxis, 1);
+ float yPosition = (float)chart.ValueToPoint(chart.SecondaryAxis, 169);
+
+ // Calculated positions for the second trackball
+ float xPosition1 = (float)chart.ValueToPoint(chart.PrimaryAxis, 6);
+ float yPosition1 = (float)chart.ValueToPoint(chart.SecondaryAxis, 170);
+
+ // Display the first trackball
+ trackball1.Display(xPosition, yPosition);
+
+ // Display the second trackball
+ trackball2.Display(xPosition1, yPosition1);
+ });
+ }
+ }
+
+ public class ChartTrackBallBehaviorExt : ChartTrackBallBehavior
+ {
+ private bool isTrackballActive = false;
+
+ public SfChart? SfChart { get; set; }
+
+ public double X { get; set; }
+ public double Y { get; set; }
+
+ protected override void OnMouseEnter(MouseEventArgs e)
+ {
+ // Get the position of the mouse pointer
+ var touchPoint = e.GetPosition(null);
+
+ // Find the nearest trackball to the mouse pointer
+ var trackball = FindNearestTrackball(touchPoint);
+
+ // Activate the trackball if it is the nearest one
+ if (trackball == this)
+ {
+ isTrackballActive = true;
+ base.OnMouseEnter(e);
+ }
+ }
+
+ protected override void OnMouseMove(MouseEventArgs e)
+ {
+ // Check if the trackball is activated
+ if (isTrackballActive)
+ {
+ // Get the position of the mouse pointer
+ var touchPoint = e.GetPosition(null);
+
+ // Display the trackball at the current mouse position
+ Display((float)touchPoint.X, (float)touchPoint.Y);
+ base.OnMouseMove(e);
+ }
+ }
+
+ protected override void OnMouseLeave(MouseEventArgs e)
+ {
+ // Deactivate the trackball
+ isTrackballActive = false;
+ }
+
+ private ChartTrackBallBehavior? FindNearestTrackball(Point touchPoint)
+ {
+ ChartTrackBallBehavior? nearestTrackball = null;
+ double minDistance = double.MaxValue;
+
+ // Iterate through all trackball behaviors to find the nearest one
+ if (SfChart != null)
+ {
+ foreach (var trackballBehaviour in SfChart.Behaviors)
+ {
+ if (trackballBehaviour is ChartTrackBallBehaviorExt trackball)
+ {
+ // Calculate the distance between the trackball and the touch point
+ double distance = Math.Sqrt(Math.Pow(trackball.X - touchPoint.X, 2) + Math.Pow(trackball.Y - touchPoint.Y, 2));
+
+ // Update the nearest trackball if the current one is closer
+ if (distance < minDistance)
+ {
+ minDistance = distance;
+ nearestTrackball = trackball;
+ }
+ }
+ }
+ }
+
+ return nearestTrackball;
+ }
+
+ public void Display(float x, float y)
+ {
+ X = x; Y = y;
+ IsActivated = true;
+ var point = new Point(x, y);
+
+ // Set the internal property for the current point
+ SetInternalProperty(typeof(ChartTrackBallBehavior), this, point, "CurrentPoint");
+
+ // Trigger the pointer position changed event
+ base.OnPointerPositionChanged();
+
+ // Activate the trackball
+ InvokeInternalMethod(typeof(ChartTrackBallBehavior), this, "Activate", IsActivated);
+ }
+
+ // Sets an internal property of an object using reflection.
+ internal static void SetInternalProperty(Type type, object obj, object value, string propertyName)
+ {
+ var properties = type.GetRuntimeProperties();
+
+ foreach (var item in properties)
+ {
+ if (item.Name == propertyName)
+ {
+ item.SetValue(obj, value);
+ break;
+ }
+ }
+ }
+
+ // Invokes an internal method of an object using reflection.
+ internal static object? InvokeInternalMethod(Type type, object obj, string methodName, params object[] args)
+ {
+ var method = type.GetTypeInfo().GetDeclaredMethod(methodName);
+ return method?.Invoke(obj, args);
+ }
+ }
+}
\ No newline at end of file
diff --git a/SfChartMultipleTrackball/SfChartMultipleTrackball/Model/Model.cs b/SfChartMultipleTrackball/SfChartMultipleTrackball/Model/Model.cs
new file mode 100644
index 0000000..1537f01
--- /dev/null
+++ b/SfChartMultipleTrackball/SfChartMultipleTrackball/Model/Model.cs
@@ -0,0 +1,8 @@
+namespace SfChartMultipleTrackball
+{
+ public class Model
+ {
+ public string? Day { get; set; }
+ public double CPULoad { get; set; }
+ }
+}
diff --git a/SfChartMultipleTrackball/SfChartMultipleTrackball/SfChartMultipleTrackball.csproj b/SfChartMultipleTrackball/SfChartMultipleTrackball/SfChartMultipleTrackball.csproj
new file mode 100644
index 0000000..52478d0
--- /dev/null
+++ b/SfChartMultipleTrackball/SfChartMultipleTrackball/SfChartMultipleTrackball.csproj
@@ -0,0 +1,15 @@
+
+
+
+ WinExe
+ net8.0-windows
+ enable
+ enable
+ true
+
+
+
+
+
+
+
diff --git a/SfChartMultipleTrackball/SfChartMultipleTrackball/SfChartMultipleTrackball.csproj.user b/SfChartMultipleTrackball/SfChartMultipleTrackball/SfChartMultipleTrackball.csproj.user
new file mode 100644
index 0000000..644b0a6
--- /dev/null
+++ b/SfChartMultipleTrackball/SfChartMultipleTrackball/SfChartMultipleTrackball.csproj.user
@@ -0,0 +1,14 @@
+
+
+
+
+
+ Designer
+
+
+
+
+ Designer
+
+
+
\ No newline at end of file
diff --git a/SfChartMultipleTrackball/SfChartMultipleTrackball/ViewModel/ViewModel.cs b/SfChartMultipleTrackball/SfChartMultipleTrackball/ViewModel/ViewModel.cs
new file mode 100644
index 0000000..d1ead73
--- /dev/null
+++ b/SfChartMultipleTrackball/SfChartMultipleTrackball/ViewModel/ViewModel.cs
@@ -0,0 +1,21 @@
+namespace SfChartMultipleTrackball
+{
+ public class ViewModel
+ {
+ public List Data { get; set; }
+
+ public ViewModel()
+ {
+ Data = new List
+ {
+ new Model { Day = "Monday", CPULoad = 35 },
+ new Model { Day = "Tuesday", CPULoad = 42 },
+ new Model { Day = "Wednesday", CPULoad = 28 },
+ new Model { Day = "Thursday", CPULoad = 40 },
+ new Model { Day = "Friday", CPULoad = 64 },
+ new Model { Day = "Saturday", CPULoad = 22 },
+ new Model { Day = "Sunday", CPULoad = 10 }
+ };
+ }
+ }
+}