Reputation: 529
I'm creating a User Control componente of type DataVisualization.Chart. Right now, he accepts 4 range limits (1 range minimum of accept value and 1 maximum, also 1 range minimum of warning value and 1 maximum warning range value). So far, so good. It works. But i need to add a TextBlock (or label, or TextBox, whatever) at the end of the LineSeries.
This is my actual LineSeries:
Now i need to make something like this:
Even if the result is not the same, i need to put a label or textblock at the end of every LineDataPoint.
Edit 1:
This is how my list of items is generated:
/// <summary>
/// Generate ItemsSource to use with chart component
/// </summary>
/// <returns>A observable collection items of ChartItems type</returns>
public ObservableCollection<ChartItems> GenerateActualValues()
{
var itemsSource = ItemsSource as IEnumerable;
if (itemsSource.IsNull())
return null;
// Get all values from ItemsSource to set Axis Y of Chart
List<Double> listAxisY = new List<Double>();
ObservableCollection<ChartItems> chartItems = new ObservableCollection<ChartItems>();
foreach (ChartItems itemSource in itemsSource)
{
listAxisY.Add(itemSource.ValueY);
chartItems.Add(new ChartItems { Name = itemSource.Name, ValueY = itemSource.ValueY, ValueXDouble = itemSource.ValueXDouble, ValueXDateTime = itemSource.ValueXDateTime, Color = itemSource.Color });
}
// Set minimum and maximum axis Y if automatic
if (AutomaticAxisY)
{
Double? maxValue;
Double? minValue;
if (listAxisY.Count > 0)
{
if (GetMaxLimitValue1(this) > listAxisY.Max())
maxValue = GetMaxLimitValue1(this);
else
maxValue = listAxisY.Max();
if (GetMinLimitValue1(this) < listAxisY.Min())
minValue = GetMinLimitValue1(this);
else
minValue = listAxisY.Min();
}
else
{
maxValue = GetMaxLimitValue1(this);
minValue = GetMinLimitValue1(this);
}
Double? increment = (maxValue - minValue) * 0.05;
MaximumAxisY = (maxValue + increment).ConvertNullDoubleToDouble();
MinimumAxisY = (minValue - increment).ConvertNullDoubleToDouble();
if (MaximumAxisY == MinimumAxisY)
{
MaximumAxisY += 1;
MinimumAxisY -= 1;
}
}
return chartItems;
}
Basically, it generate a new ItemsSource to use in my Chart. Now, to generate those extra lines:
/// <summary>
/// Generate a ItemsSource using param option informed
/// </summary>
/// <param name="option">Min1, Max1, Min2, Max2 or Target Value</param>
/// <returns>Observable Collection of ChartItems</returns>
public ObservableCollection<ChartItems> GenerateLimitValues(String option)
{
var itemsSource = ItemsSource as IEnumerable;
if (itemsSource.IsNull())
return null;
Double? valueY = 0.0;
ObservableCollection<ChartItems> chartItems = new ObservableCollection<ChartItems>();
switch (option)
{
case "Min1":
valueY = GetMinLimitValue1(this);
break;
case "Max1":
valueY = GetMaxLimitValue1(this);
break;
case "Target":
valueY = GetTargetValue(this);
break;
case "Min2":
valueY = GetMinLimitValue2(this);
break;
case "Max2":
valueY = GetMaxLimitValue2(this);
break;
}
foreach (ChartItems itemSource in itemsSource)
chartItems.Add(new ChartItems { Name = itemSource.Name, ValueY = valueY.ConvertNullDoubleToDouble(), ValueXDouble = itemSource.ValueXDouble, ValueXDateTime = itemSource.ValueXDateTime });
return chartItems;
}
Now, this is where all the things work in my Chart:
/// <summary>
/// Generates series with values for chart
/// </summary>
public void RenderizeChart()
{
while (this.chartView.Series.Count() - 1 >= 0)
this.chartView.Series.Remove(this.chartView.Series[0]);
DataPointSeries lineSeriesActualValue = null;
DataPointSeries lineSeriesMaxValue1 = null;
DataPointSeries lineSeriesMinValue1 = null;
DataPointSeries lineSeriesMaxValue2 = null;
DataPointSeries lineSeriesMinValue2 = null;
DataPointSeries lineSeriesTarget = null;
if (!ChartTypeSelectedItem.IsNull())
switch ((ChartTypes)ChartTypeSelectedItem)
{
case ChartTypes.Bar:
this.chartView.Series.Add(new BarSeries());
lineSeriesActualValue = this.chartView.Series[0] as BarSeries;
break;
case ChartTypes.Columns:
this.chartView.Series.Add(new ColumnSeries());
lineSeriesActualValue = this.chartView.Series[0] as ColumnSeries;
lineSeriesActualValue.DataPointStyle = (Style)this.Resources["ColumnDataPointStyle"];
break;
case ChartTypes.Pie:
this.chartView.Series.Add(new PieSeries());
lineSeriesActualValue = this.chartView.Series[0] as PieSeries;
break;
case ChartTypes.Lines:
this.chartView.Series.Add(new LineSeries());
lineSeriesActualValue = this.chartView.Series[0] as LineSeries;
lineSeriesActualValue.Style = (Style)this.Resources["LineSeriesStyle"];
if (!ShowPoints)
{
// Brief explanation: if user wants to hide Data Points, it's necessary to get all Setters on
// LineDataPointStyle inside xaml, clear previous style and add new Setter.
// Otherwise, it will not work, will deny changes because its sealed.
Style style = (Style)this.Resources["LineDataPointStyle"];
List<Setter> setterList = new List<Setter>();
foreach (Setter setter in style.Setters)
setterList.Add(setter);
style = new Style();
foreach (var setter in setterList)
style.Setters.Add(setter);
style.Setters.Add(new Setter(LineSeries.TemplateProperty, null));
lineSeriesActualValue.DataPointStyle = style;
}
else
lineSeriesActualValue.DataPointStyle = (Style)this.Resources["LineDataPointStyle"];
break;
case ChartTypes.Area:
this.chartView.Series.Add(new AreaSeries());
lineSeriesActualValue = this.chartView.Series[0] as AreaSeries;
break;
default:
break;
}
if (!lineSeriesActualValue.IsNull())
{
lineSeriesActualValue.IsSelectionEnabled = true;
lineSeriesActualValue.DependentValuePath = FieldForDependentValue;
lineSeriesActualValue.IndependentValuePath = FieldForIndependentValue;
lineSeriesActualValue.ItemsSource = GenerateActualValues();
// Adding a max limit to chart
if (!ItemsSource.IsNull() && ((!GetMaxLimitValue1(this).IsNull()) && !GetMaxLimitValue1(this).Equals(0.0)))
{
this.chartView.Series.Add(new LineSeries());
lineSeriesMaxValue1 = this.chartView.Series[1] as LineSeries;
Style styleMaxLineSeries = new Style();
styleMaxLineSeries.Setters.Add(new Setter(LineSeries.BackgroundProperty, new SolidColorBrush(Color.FromArgb(255, 234, 178, 15))));
styleMaxLineSeries.Setters.Add(new Setter(LineSeries.TemplateProperty, null));
lineSeriesMaxValue1.DataPointStyle = styleMaxLineSeries;
lineSeriesMaxValue1.DependentValuePath = FieldForDependentValue;
lineSeriesMaxValue1.IndependentValuePath = FieldForIndependentValue;
lineSeriesMaxValue1.ItemsSource = GenerateLimitValues("Max1");
if (this.chartView.Series.Contains(lineSeriesMaxValue1))
this.chartView.Series.Remove(lineSeriesMaxValue1);
this.chartView.Series.Add(lineSeriesMaxValue1);
}
// Adding a min limit to chart
if (!ItemsSource.IsNull() && ((!GetMinLimitValue1(this).IsNull()) && !GetMinLimitValue1(this).Equals(0.0)))
{
this.chartView.Series.Add(new LineSeries());
lineSeriesMinValue1 = this.chartView.Series[2] as LineSeries;
Style styleMinLineSeries = new Style();
styleMinLineSeries.Setters.Add(new Setter(LineSeries.BackgroundProperty, new SolidColorBrush(Color.FromArgb(255, 234, 178, 15))));
styleMinLineSeries.Setters.Add(new Setter(LineSeries.TemplateProperty, null));
lineSeriesMinValue1.DataPointStyle = styleMinLineSeries;
lineSeriesMinValue1.DependentValuePath = FieldForDependentValue;
lineSeriesMinValue1.IndependentValuePath = FieldForIndependentValue;
lineSeriesMinValue1.ItemsSource = GenerateLimitValues("Min1");
if (this.chartView.Series.Contains(lineSeriesMinValue1))
this.chartView.Series.Remove(lineSeriesMinValue1);
this.chartView.Series.Add(lineSeriesMinValue1);
}
// Adding a target value to chart
if (!ItemsSource.IsNull() && ((!GetTargetValue(this).IsNull()) && !GetTargetValue(this).Equals(0.0)))
{
this.chartView.Series.Add(new LineSeries());
lineSeriesTarget = this.chartView.Series[3] as LineSeries;
Style styleTargetLineSeries = new Style();
styleTargetLineSeries.Setters.Add(new Setter(LineSeries.BackgroundProperty, Brushes.Gray));
styleTargetLineSeries.Setters.Add(new Setter(LineSeries.TemplateProperty, null));
lineSeriesTarget.DataPointStyle = styleTargetLineSeries;
lineSeriesTarget.DependentValuePath = FieldForDependentValue;
lineSeriesTarget.IndependentValuePath = FieldForIndependentValue;
lineSeriesTarget.ItemsSource = GenerateLimitValues("Target");
if (this.chartView.Series.Contains(lineSeriesTarget))
this.chartView.Series.Remove(lineSeriesTarget);
this.chartView.Series.Add(lineSeriesTarget);
}
// Adding a max limit to chart
if (!ItemsSource.IsNull() && ((!GetMaxLimitValue2(this).IsNull()) && !GetMaxLimitValue2(this).Equals(0.0)))
{
this.chartView.Series.Add(new LineSeries());
lineSeriesMaxValue2 = this.chartView.Series[4] as LineSeries;
Style styleMaxLineSeries = new Style();
styleMaxLineSeries.Setters.Add(new Setter(LineSeries.BackgroundProperty, new SolidColorBrush(Color.FromArgb(255, 255, 0, 0))));
styleMaxLineSeries.Setters.Add(new Setter(LineSeries.TemplateProperty, null));
lineSeriesMaxValue2.DataPointStyle = styleMaxLineSeries;
lineSeriesMaxValue2.DependentValuePath = FieldForDependentValue;
lineSeriesMaxValue2.IndependentValuePath = FieldForIndependentValue;
lineSeriesMaxValue2.ItemsSource = GenerateLimitValues("Max2");
if (this.chartView.Series.Contains(lineSeriesMaxValue2))
this.chartView.Series.Remove(lineSeriesMaxValue2);
this.chartView.Series.Add(lineSeriesMaxValue2);
}
// Adding a min limit to chart
if (!ItemsSource.IsNull() && ((!GetMinLimitValue2(this).IsNull()) && !GetMinLimitValue2(this).Equals(0.0)))
{
this.chartView.Series.Add(new LineSeries());
lineSeriesMinValue2 = this.chartView.Series[5] as LineSeries;
Style styleMinLineSeries = new Style();
styleMinLineSeries.Setters.Add(new Setter(LineSeries.BackgroundProperty, new SolidColorBrush(Color.FromArgb(255, 255, 0, 0))));
styleMinLineSeries.Setters.Add(new Setter(LineSeries.TemplateProperty, null));
lineSeriesMinValue2.DataPointStyle = styleMinLineSeries;
lineSeriesMinValue2.DependentValuePath = FieldForDependentValue;
lineSeriesMinValue2.IndependentValuePath = FieldForIndependentValue;
lineSeriesMinValue2.ItemsSource = GenerateLimitValues("Min2");
if (this.chartView.Series.Contains(lineSeriesMinValue2))
this.chartView.Series.Remove(lineSeriesMinValue2);
this.chartView.Series.Add(lineSeriesMinValue2);
}
// Configure axis
if (ItemsSource.IsNull() || (((IList)ItemsSource).Count == 0))
{
foreach (var actualAxis in this.chartView.ActualAxes)
if (actualAxis.Orientation.Equals(AxisOrientation.Y))
{
(actualAxis as LinearAxis).Maximum = null;
(actualAxis as LinearAxis).Minimum = null;
(actualAxis as LinearAxis).ShowGridLines = ShowGridLinesY;
(actualAxis as LinearAxis).Visibility = Visibility.Collapsed;
}
else if (actualAxis.Orientation.Equals(AxisOrientation.X))
{
if (actualAxis is DateTimeAxis)
{
if (!FieldForIndependentValue.IsNullOrEmpty() && FieldForIndependentValue.Contains("DateTime"))
{
(actualAxis as DateTimeAxis).Maximum = null;
(actualAxis as DateTimeAxis).Minimum = null;
(actualAxis as DateTimeAxis).Visibility = Visibility.Collapsed;
}
else
(actualAxis as DateTimeAxis).Visibility = Visibility.Collapsed;
}
else
{
if (!FieldForIndependentValue.IsNullOrEmpty() && FieldForIndependentValue.Contains("Double"))
{
(actualAxis as LinearAxis).Maximum = null;
(actualAxis as LinearAxis).Minimum = null;
(actualAxis as LinearAxis).Visibility = Visibility.Collapsed;
(actualAxis as LinearAxis).ShowGridLines = ShowGridLinesX;
}
else
(actualAxis as LinearAxis).Visibility = Visibility.Collapsed;
}
}
}
else if ((this.chartView.Axes.Count > 0) && ((!ItemsSource.IsNull()) && ((IList)ItemsSource).Count > 0))
{
foreach (var actualAxis in this.chartView.ActualAxes)
{
if (actualAxis.Orientation.Equals(AxisOrientation.Y))
{
(actualAxis as LinearAxis).Maximum = null;
(actualAxis as LinearAxis).Minimum = null;
(actualAxis as LinearAxis).Maximum = MaximumAxisY;
(actualAxis as LinearAxis).Minimum = MinimumAxisY;
(actualAxis as LinearAxis).Visibility = Visibility.Visible;
(actualAxis as LinearAxis).ShowGridLines = ShowGridLinesY;
}
else if (actualAxis.Orientation.Equals(AxisOrientation.X))
{
if (actualAxis is DateTimeAxis)
{
if (!FieldForIndependentValue.IsNullOrEmpty() && FieldForIndependentValue.Contains("DateTime"))
{
(actualAxis as DateTimeAxis).Maximum = null;
(actualAxis as DateTimeAxis).Minimum = null;
(actualAxis as DateTimeAxis).Maximum = ((IList<ChartItems>)ItemsSource).Select(s => s.ValueXDateTime).LastOrDefault();
(actualAxis as DateTimeAxis).Minimum = ((IList<ChartItems>)ItemsSource).Select(s => s.ValueXDateTime).FirstOrDefault();
(actualAxis as DateTimeAxis).ShowGridLines = ShowGridLinesX;
(actualAxis as DateTimeAxis).Visibility = Visibility.Visible;
}
else
(actualAxis as DateTimeAxis).Visibility = Visibility.Collapsed;
}
else
{
if (!FieldForIndependentValue.IsNullOrEmpty() && FieldForIndependentValue.Contains("Double"))
{
(actualAxis as LinearAxis).Maximum = null;
(actualAxis as LinearAxis).Minimum = null;
if (IntervalAxisX > 0)
(actualAxis as LinearAxis).Interval = IntervalAxisX;
(actualAxis as LinearAxis).Maximum = ((IList<ChartItems>)ItemsSource).Select(s => s.ValueXDouble).LastOrDefault() + 0.5;
(actualAxis as LinearAxis).Minimum = ((IList<ChartItems>)ItemsSource).Select(s => s.ValueXDouble).FirstOrDefault() - 0.5;
(actualAxis as LinearAxis).Visibility = Visibility.Visible;
(actualAxis as LinearAxis).ShowGridLines = ShowGridLinesX;
}
else
(actualAxis as LinearAxis).Visibility = Visibility.Collapsed;
}
}
}
}
}
}
Any help will be appreciated.
Best Regards, Gustavo.
Upvotes: 1
Views: 1220
Reputation: 14037
As it has turned out, it is not difficult to add custom elements to the chart. You should retrieve the chart area, calculate positions of your elements and just add them as you add to any panel.
Here is the complete example:
private Canvas canvas;
public MainWindow()
{
InitializeComponent();
chart.Loaded += this.OnChartLoaded;
}
private void OnChartLoaded(object sender, RoutedEventArgs e)
{
var chartArea = (Panel)chart.GetType().GetProperty("ChartArea", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(chart, null);
// create a canvas to which all text blocks will be added
this.canvas = new Canvas();
chartArea.Children.Add(this.canvas);
}
public void AddAllLineLabels()
{
// add a red label
double value = 15;
var text = new TextBlock() { Text = value.ToString(), Foreground = Brushes.Red };
AddTextToCanvas(canvas, text, value);
// add a green label
value = 19;
text = new TextBlock() { Text = value.ToString(), Foreground = Brushes.Green };
AddTextToCanvas(canvas, text, value);
}
private void AddTextToCanvas(Canvas canvas, TextBlock text, double value)
{
var valuesAxis = chart.ActualAxes.OfType<LinearAxis>().FirstOrDefault(ax => ax.Orientation == AxisOrientation.Y);
var min = valuesAxis.ActualMinimum.Value;
var max = valuesAxis.ActualMaximum.Value;
var maxPixels = valuesAxis.ActualHeight;
var valuePixels = (value - min) / (max - min) * maxPixels; // from the bottom edge to the value in pixels
Canvas.SetRight(text, 5); // 5 is a padding from the right edge, you can use any number
Canvas.SetBottom(text, valuePixels);
canvas.Children.Add(text);
}
If you call the AddAllLineLabels
method, it will display the red number 15 and green number 19.
Upvotes: 1