windowsforms-2504-controls-and-libraries-chart-control-examples-creating-charts-appearance-customization-how-to-custom-draw-series-points.md
This example demonstrates how to use the ChartControl.CustomDrawSeriesPoint event to modify the legend markers of nested donut series points.
Assign a custom legend marker to the e.DXLegendMarkerImage property. Set the e.DisposeLegendMarkerImage property to true to avoid memory leaks. To customize options used to draw the series point, cast e.SeriesDrawOptions to the DrawOptions class descendant that stores draw options of the required series view type.
using CustomSeriesPointDrawingSample.Model;
using DevExpress.Drawing;
using DevExpress.XtraCharts;
using System;
using System.Collections.Generic;
using System.Data;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.IO;
using System.Linq;
using System.Windows.Forms;
namespace CustomSeriesPointDrawingSample {
public partial class Form1 : Form {
object trackedPointArgument;
Dictionary<string, DXImage> photoCache = new Dictionary<string, DXImage>();
#region #Constants
const int borderSize = 5;
const int scaledPhotoWidth = 48;
const int scaledPhotoHeight = 51;
// Width and height of scaled photo with border.
const int totalWidth = 58;
const int totalHeight = 61;
// Rects required to create a custom legend series marker.
static readonly Rectangle photoRect = new Rectangle(
borderSize, borderSize,
scaledPhotoWidth, scaledPhotoHeight);
static readonly Rectangle totalRect = new Rectangle(
0, 0,
totalWidth, totalHeight);
#endregion
public Form1() {
InitializeComponent();
}
#region #ChartPreparation
private void Form1_Load(object sender, EventArgs e) {
chart.CustomDrawSeriesPoint += OnCustomDrawSeriesPoint;
chart.BoundDataChanged += OnBoundDataChanged;
chart.ObjectHotTracked += OnObjectHotTracked;
using (var context = new NwindDbContext()) {
chart.DataSource = PrepareDataSource(context.Orders);
InitPhotoCache(context.Employees);
}
chart.SeriesDataMember = "Year";
chart.SeriesTemplate.ArgumentDataMember = "Employee";
chart.SeriesTemplate.ValueDataMembers.AddRange("Value");
chart.SeriesTemplate.ToolTipPointPattern = "{S}: {A} ({VP:P})";
chart.SeriesTemplate.SeriesPointsSorting = SortingMode.Ascending;
}
#endregion
#region #AutogeneratedSeriesModifying
private void OnBoundDataChanged(object sender, EventArgs e) {
if (chart.Series.Count <= 1) return;
for (int i = 1; i < chart.Series.Count; ++i)
chart.Series[i].ShowInLegend = false;
}
#endregion
#region #CustomPointDrawing
private void OnCustomDrawSeriesPoint(object sender, CustomDrawSeriesPointEventArgs e) {
// Design a series marker image.
DXBitmap image = new DXBitmap(totalWidth, totalHeight);
bool isSelected = trackedPointArgument != null && e.SeriesPoint.Argument.Equals(trackedPointArgument);
using (DXGraphics graphics = DXGraphics.FromImage(image)) {
using (var fillBrush = isSelected ? (DXBrush)new DXHatchBrush(DXHatchStyle.DarkDownwardDiagonal,
e.LegendDrawOptions.Color,
e.LegendDrawOptions.ActualColor2)
: (DXBrush)new DXSolidBrush(e.LegendDrawOptions.Color)) {
graphics.FillRectangle(fillBrush, totalRect);
}
DXImage photo;
if (photoCache.TryGetValue(e.SeriesPoint.Argument, out photo))
graphics.DrawImage(photo, photoRect);
}
e.DXLegendMarkerImage = image;
e.DisposeLegendMarkerImage = true;
PieDrawOptions options = e.SeriesDrawOptions as PieDrawOptions;
if (isSelected && options != null) {
options.FillStyle.FillMode = DevExpress.XtraCharts.FillMode.Hatch;
((HatchFillOptions)options.FillStyle.Options).HatchStyle = HatchStyle.DarkDownwardDiagonal;
}
}
#endregion
private void OnObjectHotTracked(object sender, HotTrackEventArgs e) {
trackedPointArgument = e.HitInfo.InSeriesPoint ? e.HitInfo.SeriesPoint.Argument : null;
chart.Invalidate();
}
void InitPhotoCache(IEnumerable<Employee> employees) {
photoCache.Clear();
foreach (var employee in employees) {
using (MemoryStream stream = new MemoryStream(employee.Photo)) {
if (!photoCache.ContainsKey(employee.FullName))
photoCache.Add(employee.FullName, DXImage.FromStream(stream));
}
}
}
List<SalesPoint> PrepareDataSource(IEnumerable<Order> orders) {
var query = from o in orders
group o by new {
Year = o.OrderDate.Year,
Employee = o.Employee.FirstName + " " + o.Employee.LastName
}
into g
select new {
Employee = g.Key.Employee,
Year = g.Key.Year,
Values = g.Select(o => o.Freight.HasValue ? o.Freight.Value : 0)
};
List<SalesPoint> points = new List<SalesPoint>();
foreach (var item in query) {
points.Add(new SalesPoint {
Employee = item.Employee,
Year = item.Year,
Value = item.Values.Aggregate((d1, d2) => d1 + d2)
});
}
return points;
}
}
}
class SalesPoint {
public string Employee { get; set; }
public int Year { get; set; }
public decimal Value { get; set; }
}
Imports CustomSeriesPointDrawingSample.Model
Imports DevExpress.Drawing
Imports DevExpress.XtraCharts
Imports System
Imports System.Collections.Generic
Imports System.Data
Imports System.Drawing
Imports System.Drawing.Drawing2D
Imports System.IO
Imports System.Linq
Imports System.Windows.Forms
Namespace CustomSeriesPointDrawingSample
Public Partial Class Form1
Inherits System.Windows.Forms.Form
Private trackedPointArgument As Object
Private photoCache As System.Collections.Generic.Dictionary(Of String, DevExpress.Drawing.DXImage) = New System.Collections.Generic.Dictionary(Of String, DevExpress.Drawing.DXImage)()
#Region "#Constants"
Const borderSize As Integer = 5
Const scaledPhotoWidth As Integer = 48
Const scaledPhotoHeight As Integer = 51
' Width and height of scaled photo with border.
Const totalWidth As Integer = 58
Const totalHeight As Integer = 61
' Rects required to create a custom legend series marker.
Private Shared ReadOnly photoRect As System.Drawing.Rectangle = New System.Drawing.Rectangle(CustomSeriesPointDrawingSample.Form1.borderSize, CustomSeriesPointDrawingSample.Form1.borderSize, CustomSeriesPointDrawingSample.Form1.scaledPhotoWidth, CustomSeriesPointDrawingSample.Form1.scaledPhotoHeight)
Private Shared ReadOnly totalRect As System.Drawing.Rectangle = New System.Drawing.Rectangle(0, 0, CustomSeriesPointDrawingSample.Form1.totalWidth, CustomSeriesPointDrawingSample.Form1.totalHeight)
#End Region
Public Sub New()
Me.InitializeComponent()
End Sub
#Region "#ChartPreparation"
Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs)
AddHandler Me.chart.CustomDrawSeriesPoint, AddressOf Me.OnCustomDrawSeriesPoint
AddHandler Me.chart.BoundDataChanged, AddressOf Me.OnBoundDataChanged
AddHandler Me.chart.ObjectHotTracked, AddressOf Me.OnObjectHotTracked
Using context = New CustomSeriesPointDrawingSample.Model.NwindDbContext()
Me.chart.DataSource = Me.PrepareDataSource(context.Orders)
Me.InitPhotoCache(context.Employees)
End Using
Me.chart.SeriesDataMember = "Year"
Me.chart.SeriesTemplate.ArgumentDataMember = "Employee"
Me.chart.SeriesTemplate.ValueDataMembers.AddRange("Value")
Me.chart.SeriesTemplate.ToolTipPointPattern = "{S}: {A} ({VP:P})"
Me.chart.SeriesTemplate.SeriesPointsSorting = DevExpress.XtraCharts.SortingMode.Ascending
End Sub
#End Region
#Region "#AutogeneratedSeriesModifying"
Private Sub OnBoundDataChanged(ByVal sender As Object, ByVal e As System.EventArgs)
If Me.chart.Series.Count <= 1 Then Return
Dim i As Integer = 1
While i < Me.chart.Series.Count
Me.chart.Series(CInt((i))).ShowInLegend = False
Call System.Threading.Interlocked.Increment(i)
End While
End Sub
#End Region
#Region "#CustomPointDrawing"
Private Sub OnCustomDrawSeriesPoint(ByVal sender As Object, ByVal e As DevExpress.XtraCharts.CustomDrawSeriesPointEventArgs)
' Design a series marker image.
Dim image As DevExpress.Drawing.DXBitmap = New DevExpress.Drawing.DXBitmap(CustomSeriesPointDrawingSample.Form1.totalWidth, CustomSeriesPointDrawingSample.Form1.totalHeight)
Dim isSelected As Boolean = Me.trackedPointArgument IsNot Nothing AndAlso e.SeriesPoint.Argument.Equals(Me.trackedPointArgument)
Using graphics As DevExpress.Drawing.DXGraphics = DevExpress.Drawing.DXGraphics.FromImage(image)
Using fillBrush = If(isSelected, CType(New DevExpress.Drawing.DXHatchBrush(DevExpress.Drawing.DXHatchStyle.DarkDownwardDiagonal, e.LegendDrawOptions.Color, e.LegendDrawOptions.ActualColor2), DevExpress.Drawing.DXBrush), CType(New DevExpress.Drawing.DXSolidBrush(e.LegendDrawOptions.Color), DevExpress.Drawing.DXBrush))
graphics.FillRectangle(fillBrush, CustomSeriesPointDrawingSample.Form1.totalRect)
End Using
Dim photo As DevExpress.Drawing.DXImage
If Me.photoCache.TryGetValue(e.SeriesPoint.Argument, photo) Then graphics.DrawImage(photo, CustomSeriesPointDrawingSample.Form1.photoRect)
End Using
e.DXLegendMarkerImage = image
e.DisposeLegendMarkerImage = True
Dim options As DevExpress.XtraCharts.PieDrawOptions = TryCast(e.SeriesDrawOptions, DevExpress.XtraCharts.PieDrawOptions)
If isSelected AndAlso options IsNot Nothing Then
options.FillStyle.FillMode = DevExpress.XtraCharts.FillMode.Hatch
CType(options.FillStyle.Options, DevExpress.XtraCharts.HatchFillOptions).HatchStyle = System.Drawing.Drawing2D.HatchStyle.DarkDownwardDiagonal
End If
End Sub
#End Region
Private Sub OnObjectHotTracked(ByVal sender As Object, ByVal e As DevExpress.XtraCharts.HotTrackEventArgs)
Me.trackedPointArgument = If(e.HitInfo.InSeriesPoint, e.HitInfo.SeriesPoint.Argument, Nothing)
Me.chart.Invalidate()
End Sub
Private Sub InitPhotoCache(ByVal employees As System.Collections.Generic.IEnumerable(Of CustomSeriesPointDrawingSample.Model.Employee))
Me.photoCache.Clear()
For Each employee In employees
Using stream As System.IO.MemoryStream = New System.IO.MemoryStream(employee.Photo)
If Not Me.photoCache.ContainsKey(employee.FullName) Then Me.photoCache.Add(employee.FullName, DevExpress.Drawing.DXImage.FromStream(stream))
End Using
Next
End Sub
Private Function PrepareDataSource(ByVal orders As System.Collections.Generic.IEnumerable(Of CustomSeriesPointDrawingSample.Model.Order)) As List(Of SalesPoint)
Dim query = From o In orders Group o By __groupByKey1__ = New With {.Year = o.OrderDate.Year, .Employee = o.Employee.FirstName & " " & o.Employee.LastName} Into g = Group Select New With {.Employee = __groupByKey1__.Employee, .Year = __groupByKey1__.Year, .Values = g.[Select](Function(o) If(o.Freight.HasValue, o.Freight.Value, 0))}
Dim points As System.Collections.Generic.List(Of SalesPoint) = New System.Collections.Generic.List(Of SalesPoint)()
For Each item In query
points.Add(New SalesPoint With {.Employee = item.Employee, .Year = item.Year, .Value = item.Values.Aggregate(Function(d1, d2) d1 + d2)})
Next
Return points
End Function
End Class
End Namespace
Friend Class SalesPoint
Public Property Employee As String
Public Property Year As Integer
Public Property Value As Decimal
End Class
See Also