AxisLabels.cs 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898
  1. // Licensed to the .NET Foundation under one or more agreements.
  2. // The .NET Foundation licenses this file to you under the MIT license.
  3. // See the LICENSE file in the project root for more information.
  4. //
  5. // Purpose: Base class for the Axis class which defines axis
  6. // labels related properties and methods.
  7. //
  8. using System;
  9. using System.Collections;
  10. using System.Collections.Generic;
  11. using System.ComponentModel;
  12. using System.Drawing.Design;
  13. using FastReport.DataVisualization.Charting.ChartTypes;
  14. using FastReport.DataVisualization.Charting.Utilities;
  15. namespace FastReport.DataVisualization.Charting
  16. {
  17. /// <summary>
  18. /// The Axis class provides functionality for
  19. /// drawing axis labels.
  20. /// </summary>
  21. public partial class Axis
  22. {
  23. #region Fields
  24. // Custom Labels collection
  25. private CustomLabelsCollection _customLabels = null;
  26. #endregion
  27. #region Axis labels properties
  28. /// <summary>
  29. /// Gets or sets the style of the label.
  30. /// </summary>
  31. [
  32. SRCategory("CategoryAttributeLabels"),
  33. Bindable(true),
  34. NotifyParentPropertyAttribute(true),
  35. SRDescription("DescriptionAttributeLabelStyle"),
  36. DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
  37. TypeConverter(typeof(NoNameExpandableObjectConverter))
  38. ]
  39. public LabelStyle LabelStyle
  40. {
  41. get
  42. {
  43. return labelStyle;
  44. }
  45. set
  46. {
  47. labelStyle = value;
  48. labelStyle.Axis = (Axis)this;
  49. this.Invalidate();
  50. }
  51. }
  52. /// <summary>
  53. /// Gets a collection of custom labels.
  54. /// </summary>
  55. [
  56. SRCategory("CategoryAttributeLabels"),
  57. Bindable(true),
  58. SRDescription("DescriptionAttributeCustomLabels"),
  59. DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
  60. #if DESIGNER
  61. Editor(typeof(ChartCollectionEditor), typeof(UITypeEditor))
  62. #endif
  63. ]
  64. public CustomLabelsCollection CustomLabels
  65. {
  66. get
  67. {
  68. return _customLabels;
  69. }
  70. }
  71. #endregion
  72. #region Axis labels methods
  73. /// <summary>
  74. /// Indicates that custom grid lines should be painted.
  75. /// </summary>
  76. /// <returns>Indicates that custom grid lines should be painted.</returns>
  77. internal bool IsCustomGridLines()
  78. {
  79. if(this.CustomLabels.Count > 0)
  80. {
  81. // Check if at least one custom label has a flag set
  82. foreach(CustomLabel label in this.CustomLabels)
  83. {
  84. if((label.GridTicks & GridTickTypes.Gridline) == GridTickTypes.Gridline)
  85. {
  86. return true;
  87. }
  88. }
  89. }
  90. return false;
  91. }
  92. /// <summary>
  93. /// Indicates that custom tick marks should be painted.
  94. /// </summary>
  95. /// <returns>Indicates that custom tick marks should be painted.</returns>
  96. internal bool IsCustomTickMarks()
  97. {
  98. if(this.CustomLabels.Count > 0)
  99. {
  100. // Check if at least one custom label has a flag set
  101. foreach(CustomLabel label in this.CustomLabels)
  102. {
  103. if((label.GridTicks & GridTickTypes.TickMark) == GridTickTypes.TickMark)
  104. {
  105. return true;
  106. }
  107. }
  108. }
  109. return false;
  110. }
  111. /// <summary>
  112. /// Gets the type of the axis.
  113. /// </summary>
  114. /// <value>The type of the axis.</value>
  115. internal AxisType GetAxisType()
  116. {
  117. if (this.axisType == AxisName.X || this.axisType == AxisName.Y)
  118. {
  119. return AxisType.Primary;
  120. }
  121. else
  122. {
  123. return AxisType.Secondary;
  124. }
  125. }
  126. /// <summary>
  127. /// Gets the axis series.
  128. /// </summary>
  129. /// <returns></returns>
  130. internal ArrayList GetAxisSeries()
  131. {
  132. ArrayList dataSeries = new ArrayList();
  133. // check for attached series.
  134. foreach (string seriesName in this.ChartArea.Series)
  135. {
  136. Series series = this.Common.DataManager.Series[seriesName];
  137. if (this.axisType == AxisName.X || this.axisType == AxisName.X2)
  138. {
  139. if (series.XAxisType == this.GetAxisType())
  140. {
  141. dataSeries.Add(series);
  142. }
  143. }
  144. else
  145. {
  146. if (series.YAxisType == this.GetAxisType())
  147. {
  148. dataSeries.Add(series);
  149. }
  150. }
  151. }
  152. return dataSeries;
  153. }
  154. /// <summary>
  155. /// Gets the other (primary/secondary) axis.
  156. /// </summary>
  157. /// <returns></returns>
  158. internal Axis GetOtherTypeAxis()
  159. {
  160. return ChartArea.GetAxis(
  161. this.axisType,
  162. this.GetAxisType() == AxisType.Primary ? AxisType.Secondary : AxisType.Primary,
  163. String.Empty
  164. );
  165. }
  166. /// <summary>
  167. /// Checks if the other (primary/secondary) axis has custom labels labels.
  168. /// These labels will be added if this axis has no series attached and no custom labels.
  169. /// This works only on category axes.
  170. /// </summary>
  171. internal void PostFillLabels()
  172. {
  173. foreach (CustomLabel label in this.CustomLabels)
  174. {
  175. if (label.customLabel)
  176. {
  177. return;
  178. }
  179. }
  180. // Labels are disabled for this axis
  181. if (
  182. !this.LabelStyle.Enabled ||
  183. !this.enabled ||
  184. !String.IsNullOrEmpty(((Axis)this).SubAxisName) ||
  185. this.axisType == AxisName.Y ||
  186. this.axisType == AxisName.Y2
  187. )
  188. {
  189. return;
  190. }
  191. // check if no series attached.
  192. if (this.GetAxisSeries().Count > 0)
  193. {
  194. return;
  195. }
  196. this.CustomLabels.Clear();
  197. foreach (CustomLabel label in this.GetOtherTypeAxis().CustomLabels)
  198. {
  199. this.CustomLabels.Add(label.Clone());
  200. }
  201. }
  202. /// <summary>
  203. /// Fill labels from data from data manager or
  204. /// from axis scale.
  205. /// </summary>
  206. /// <param name="removeFirstRow">True if first row of auto generated labels must be removed.</param>
  207. internal void FillLabels(bool removeFirstRow)
  208. {
  209. #if SUBAXES
  210. // Process all sub-axis
  211. foreach(SubAxis subAxis in ((Axis)this).SubAxes)
  212. {
  213. subAxis.FillLabels(true);
  214. }
  215. #endif // SUBAXES
  216. // Labels are disabled for this axis
  217. if( !this.LabelStyle.Enabled || !this.enabled )
  218. {
  219. return;
  220. }
  221. // For circular chart area fill only Y axis labels
  222. if(this.ChartArea != null && this.ChartArea.chartAreaIsCurcular)
  223. {
  224. if(this.axisType != AxisName.Y)
  225. {
  226. ICircularChartType type = this.ChartArea.GetCircularChartType();
  227. if(type == null || !type.XAxisLabelsSupported())
  228. {
  229. return;
  230. }
  231. }
  232. }
  233. // Check if the custom labels exist
  234. bool customLabelsFlag = false;
  235. foreach( CustomLabel lab in CustomLabels )
  236. {
  237. if( lab.customLabel )
  238. {
  239. if( lab.RowIndex == 0 ||
  240. this.ChartArea.chartAreaIsCurcular)
  241. {
  242. customLabelsFlag = true;
  243. }
  244. }
  245. }
  246. // Remove the first row of labels if custom labels not exist
  247. if(removeFirstRow)
  248. {
  249. if( customLabelsFlag == false )
  250. {
  251. for( int index = 0; index < CustomLabels.Count; index++ )
  252. {
  253. if( CustomLabels[index].RowIndex == 0 )
  254. {
  255. CustomLabels.RemoveAt( index );
  256. index = -1;
  257. }
  258. }
  259. }
  260. else
  261. {
  262. return;
  263. }
  264. }
  265. // Get data series for this axis.
  266. List<string> dataSeries = null;
  267. switch( axisType )
  268. {
  269. case AxisName.X:
  270. dataSeries = ChartArea.GetXAxesSeries( AxisType.Primary, ((Axis)this).SubAxisName );
  271. break;
  272. case AxisName.Y:
  273. dataSeries = ChartArea.GetYAxesSeries( AxisType.Primary, ((Axis)this).SubAxisName );
  274. break;
  275. case AxisName.X2:
  276. dataSeries = ChartArea.GetXAxesSeries( AxisType.Secondary, ((Axis)this).SubAxisName );
  277. break;
  278. case AxisName.Y2:
  279. dataSeries = ChartArea.GetYAxesSeries( AxisType.Secondary, ((Axis)this).SubAxisName );
  280. break;
  281. }
  282. // There aren't data series connected with this axis.
  283. if( dataSeries.Count == 0 )
  284. {
  285. return;
  286. }
  287. //Let's convert the ArrayList of the series names into to string[]
  288. string[] dataSeriesNames = new string[dataSeries.Count];
  289. for (int i = 0; i < dataSeries.Count; i++)
  290. dataSeriesNames[i] = (string)dataSeries[i];
  291. // Check if series X values all set to zeros
  292. bool seriesXValuesZeros = ChartHelper.SeriesXValuesZeros(this.Common, dataSeriesNames);
  293. // Check if series is indexed (All X values zeros or IsXValueIndexed flag set)
  294. bool indexedSeries = true;
  295. if (!seriesXValuesZeros)
  296. {
  297. indexedSeries = ChartHelper.IndexedSeries(this.Common, dataSeriesNames);
  298. }
  299. // Show End Labels
  300. int endLabels = 0;
  301. if( labelStyle.IsEndLabelVisible )
  302. {
  303. endLabels = 1;
  304. }
  305. // Get chart type of the first series
  306. IChartType chartType = Common.ChartTypeRegistry.GetChartType( ChartArea.GetFirstSeries().ChartTypeName );
  307. bool fromSeries = false;
  308. if( !chartType.RequireAxes )
  309. {
  310. return;
  311. }
  312. else if( axisType == AxisName.Y || axisType == AxisName.Y2 )
  313. {
  314. fromSeries = false;
  315. }
  316. else
  317. {
  318. fromSeries = true;
  319. }
  320. // X values from data points are not 0.
  321. if (fromSeries && !ChartHelper.SeriesXValuesZeros(this.Common, dataSeries.ToArray()))
  322. {
  323. fromSeries = false;
  324. }
  325. // X values from data points are not 0.
  326. if( fromSeries && ( labelStyle.GetIntervalOffset() != 0 || labelStyle.GetInterval() != 0 ) )
  327. {
  328. fromSeries = false;
  329. }
  330. // Get value type
  331. ChartValueType valueType;
  332. if( axisType == AxisName.X || axisType == AxisName.X2 )
  333. {
  334. // If X value is indexed the type is always String. So we use indexed type instead
  335. valueType = Common.DataManager.Series[dataSeries[0]].indexedXValueType;
  336. }
  337. else
  338. {
  339. valueType = Common.DataManager.Series[dataSeries[0]].YValueType;
  340. }
  341. if( labelStyle.GetIntervalType() != DateTimeIntervalType.Auto &&
  342. labelStyle.GetIntervalType() != DateTimeIntervalType.Number )
  343. {
  344. if (valueType != ChartValueType.Time &&
  345. valueType != ChartValueType.Date &&
  346. valueType != ChartValueType.DateTimeOffset)
  347. {
  348. valueType = ChartValueType.DateTime;
  349. }
  350. }
  351. // ***********************************
  352. // Pre calculate some values
  353. // ***********************************
  354. double viewMaximum = this.ViewMaximum;
  355. double viewMinimum = this.ViewMinimum;
  356. // ***********************************
  357. // Labels are filled from data series.
  358. // ***********************************
  359. if( fromSeries )
  360. {
  361. int numOfPoints;
  362. numOfPoints = Common.DataManager.GetNumberOfPoints( dataSeries.ToArray() );
  363. // Show end labels
  364. if( endLabels == 1 )
  365. {
  366. // min position
  367. CustomLabels.Add( - 0.5, 0.5, ValueConverter.FormatValue(
  368. this.Common.Chart,
  369. this,
  370. null,
  371. 0.0,
  372. this.LabelStyle.Format,
  373. valueType,
  374. ChartElementType.AxisLabels),
  375. false);
  376. }
  377. // Labels from point position
  378. for( int point = 0; point < numOfPoints; point++ )
  379. {
  380. CustomLabels.Add( ((double)point)+ 0.5, ((double)point)+ 1.5,
  381. ValueConverter.FormatValue(
  382. this.Common.Chart,
  383. this,
  384. null,
  385. point + 1,
  386. this.LabelStyle.Format,
  387. valueType,
  388. ChartElementType.AxisLabels),
  389. false);
  390. }
  391. // Show end labels
  392. if( endLabels == 1 )
  393. {
  394. // max position
  395. CustomLabels.Add( ((double)numOfPoints)+ 0.5, ((double)numOfPoints)+ 1.5,
  396. ValueConverter.FormatValue(
  397. this.Common.Chart,
  398. this,
  399. null,
  400. numOfPoints + 1,
  401. this.LabelStyle.Format,
  402. valueType,
  403. ChartElementType.AxisLabels),
  404. false);
  405. }
  406. int pointIndx;
  407. foreach( string seriesIndx in dataSeries )
  408. {
  409. // End labels enabled
  410. if( endLabels == 1 )
  411. pointIndx = 1;
  412. else
  413. pointIndx = 0;
  414. // Set labels from data points labels
  415. foreach( DataPoint dataPoint in Common.DataManager.Series[ seriesIndx ].Points )
  416. {
  417. // Find first row of labels
  418. while( CustomLabels[pointIndx].RowIndex > 0 )
  419. {
  420. pointIndx++;
  421. }
  422. // Add X labels
  423. if( axisType == AxisName.X || axisType == AxisName.X2 )
  424. {
  425. if( dataPoint.AxisLabel.Length > 0 )
  426. {
  427. CustomLabels[pointIndx].Text = dataPoint.AxisLabel;
  428. }
  429. }
  430. pointIndx++;
  431. }
  432. }
  433. }
  434. // ***********************************
  435. // Labels are filled from axis scale.
  436. // ***********************************
  437. else
  438. {
  439. if( viewMinimum == viewMaximum )
  440. return;
  441. double labValue; // Value, which will be converted to text and used for, labels.
  442. double beginPosition; // Begin position for a label
  443. double endPosition; // End position for a label
  444. double start; // Start position for all labels
  445. // Get first series attached to this axis
  446. Series axisSeries = null;
  447. if(axisType == AxisName.X || axisType == AxisName.X2)
  448. {
  449. List<string> seriesArray = ChartArea.GetXAxesSeries((axisType == AxisName.X) ? AxisType.Primary : AxisType.Secondary, ((Axis)this).SubAxisName);
  450. if(seriesArray.Count > 0)
  451. {
  452. axisSeries = Common.DataManager.Series[seriesArray[0]];
  453. if(axisSeries != null && !axisSeries.IsXValueIndexed)
  454. {
  455. axisSeries = null;
  456. }
  457. }
  458. }
  459. // ***********************************
  460. // Check if the AJAX zooming and scrolling mode is enabled.
  461. // Labels are filled slightly different in this case.
  462. // ***********************************
  463. DateTimeIntervalType offsetType = (labelStyle.GetIntervalOffsetType() == DateTimeIntervalType.Auto) ? labelStyle.GetIntervalType() : labelStyle.GetIntervalOffsetType();
  464. // By default start is equal to minimum
  465. start = viewMinimum;
  466. // Adjust start position depending on the interval type
  467. if(!this.ChartArea.chartAreaIsCurcular ||
  468. this.axisType == AxisName.Y ||
  469. this.axisType == AxisName.Y2 )
  470. {
  471. start = ChartHelper.AlignIntervalStart(start, labelStyle.GetInterval(), labelStyle.GetIntervalType(), axisSeries);
  472. }
  473. // Move start if there is start position
  474. if( labelStyle.GetIntervalOffset() != 0 && axisSeries == null)
  475. {
  476. start += ChartHelper.GetIntervalSize(start, labelStyle.GetIntervalOffset(),
  477. offsetType, axisSeries, 0, DateTimeIntervalType.Number, true, false);
  478. }
  479. // ***************************************
  480. // Date type
  481. // ***************************************
  482. if( valueType == ChartValueType.DateTime ||
  483. valueType == ChartValueType.Date ||
  484. valueType == ChartValueType.Time ||
  485. valueType == ChartValueType.DateTimeOffset ||
  486. axisSeries != null)
  487. {
  488. double position = start;
  489. double dateInterval;
  490. // Too many labels
  491. if ((viewMaximum - start) / ChartHelper.GetIntervalSize(start, labelStyle.GetInterval(), labelStyle.GetIntervalType(), axisSeries, 0, DateTimeIntervalType.Number, true) > ChartHelper.MaxNumOfGridlines)
  492. return;
  493. int counter = 0;
  494. double endLabelMaxPosition = viewMaximum - ChartHelper.GetIntervalSize(viewMaximum, labelStyle.GetInterval(), labelStyle.GetIntervalType(), axisSeries, labelStyle.GetIntervalOffset(), offsetType, true) / 2f;
  495. double endLabelMinPosition = viewMinimum + ChartHelper.GetIntervalSize(viewMinimum, labelStyle.GetInterval(), labelStyle.GetIntervalType(), axisSeries, labelStyle.GetIntervalOffset(), offsetType, true) / 2f;
  496. while( (decimal)position <= (decimal)viewMaximum )
  497. {
  498. dateInterval = ChartHelper.GetIntervalSize(position, labelStyle.GetInterval(), labelStyle.GetIntervalType(), axisSeries, labelStyle.GetIntervalOffset(), offsetType, true);
  499. labValue = position;
  500. // For IsLogarithmic axes
  501. if( this.IsLogarithmic )
  502. {
  503. labValue = Math.Pow( this.logarithmBase, labValue );
  504. }
  505. // Check if we do not exceed max number of elements
  506. if (counter++ > ChartHelper.MaxNumOfGridlines)
  507. {
  508. break;
  509. }
  510. if (endLabels == 0 && position >= endLabelMaxPosition)
  511. {
  512. break;
  513. }
  514. beginPosition = position - dateInterval * 0.5;
  515. endPosition = position + dateInterval * 0.5;
  516. if(endLabels == 0 && position <= endLabelMinPosition)
  517. {
  518. position += dateInterval;
  519. continue;
  520. }
  521. if( (decimal)beginPosition > (decimal)viewMaximum )
  522. {
  523. position += dateInterval;
  524. continue;
  525. }
  526. // NOTE: Fixes issue #6466
  527. // Following code is removed due to the issues caused by the rounding error
  528. //if( (((decimal)beginPosition + (decimal)endPosition) / 2.0m) < (decimal)viewMinimum )
  529. //{
  530. // position += dateInterval;
  531. // continue;
  532. //}
  533. //if ((decimal)viewMaximum < (((decimal)beginPosition + (decimal)endPosition) / 2m))
  534. //{
  535. // position += dateInterval;
  536. // continue;
  537. //}
  538. string pointLabel = GetPointLabel( dataSeries, labValue, !seriesXValuesZeros, indexedSeries );
  539. if( pointLabel.Length == 0 )
  540. {
  541. // Do not draw last label for indexed series
  542. if( position <= this.maximum )
  543. {
  544. // Add a label to the collection
  545. if( position != this.maximum || !Common.DataManager.Series[ dataSeries[0] ].IsXValueIndexed )
  546. {
  547. CustomLabels.Add( beginPosition,
  548. endPosition,
  549. ValueConverter.FormatValue(
  550. this.Common.Chart,
  551. this,
  552. null,
  553. labValue,
  554. this.LabelStyle.Format,
  555. valueType,
  556. ChartElementType.AxisLabels),
  557. false);
  558. }
  559. }
  560. }
  561. else
  562. {
  563. // Add a label to the collection
  564. CustomLabels.Add( beginPosition,
  565. endPosition,
  566. pointLabel,
  567. false);
  568. }
  569. position += dateInterval;
  570. }
  571. }
  572. else
  573. {
  574. // ***************************************
  575. // Scale value type
  576. // ***************************************
  577. // Show First label if Start Label position is used
  578. if( start != viewMinimum )
  579. endLabels = 1;
  580. // Set labels
  581. int labelCounter = 0;
  582. for (double position = start - endLabels * labelStyle.GetInterval(); position < viewMaximum - 1.5 * labelStyle.GetInterval() * (1 - endLabels); position = (double)((decimal)position + (decimal)labelStyle.GetInterval()))
  583. {
  584. // Prevent endless loop that may be caused by very small interval
  585. // and double/decimal rounding errors
  586. ++labelCounter;
  587. if(labelCounter > ChartHelper.MaxNumOfGridlines)
  588. {
  589. break;
  590. }
  591. labValue = (double)((decimal)position + (decimal)labelStyle.GetInterval());
  592. // This line is introduce because sometimes 0 value will appear as
  593. // very small value close to zero.
  594. double inter = Math.Log(labelStyle.GetInterval());
  595. double valu = Math.Log(Math.Abs(labValue));
  596. int digits = (int)Math.Abs(inter)+5;
  597. if( digits > 15 )
  598. {
  599. digits = 15;
  600. }
  601. if( Math.Abs(inter) < Math.Abs(valu)-5 )
  602. {
  603. labValue = Math.Round(labValue,digits);
  604. }
  605. // Too many labels
  606. if( ( viewMaximum - start ) / labelStyle.GetInterval() > ChartHelper.MaxNumOfGridlines )
  607. {
  608. return;
  609. }
  610. // For IsLogarithmic axes
  611. if( this.IsLogarithmic )
  612. labValue = Math.Pow( this.logarithmBase, labValue );
  613. beginPosition = (double)((decimal)position + (decimal)labelStyle.GetInterval() * 0.5m);
  614. endPosition = (double)((decimal)position + (decimal)labelStyle.GetInterval() * 1.5m);
  615. if( (decimal)beginPosition > (decimal)viewMaximum )
  616. {
  617. continue;
  618. }
  619. // Show End label if Start Label position is used
  620. // Use decimal type to solve rounding issues
  621. if( (decimal)(( beginPosition + endPosition )/2.0) > (decimal)viewMaximum )
  622. {
  623. continue;
  624. }
  625. string pointLabel = GetPointLabel( dataSeries, labValue, !seriesXValuesZeros, indexedSeries );
  626. if( pointLabel.Length > 15 && labValue < 0.000001)
  627. {
  628. labValue = 0.0;
  629. }
  630. if( pointLabel.Length == 0 )
  631. {
  632. // Do not draw last label for indexed series
  633. if( !(Common.DataManager.Series[ dataSeries[0] ].IsXValueIndexed && position > this.maximum) )
  634. {
  635. // Add a label to the collection
  636. CustomLabels.Add( beginPosition,
  637. endPosition,
  638. ValueConverter.FormatValue(
  639. this.Common.Chart,
  640. this,
  641. null,
  642. labValue,
  643. this.LabelStyle.Format,
  644. valueType,
  645. ChartElementType.AxisLabels),
  646. false);
  647. }
  648. }
  649. else
  650. {
  651. // Add a label to the collection
  652. CustomLabels.Add( beginPosition,
  653. endPosition,
  654. pointLabel,
  655. false);
  656. }
  657. }
  658. }
  659. }
  660. }
  661. /// <summary>
  662. /// This method checks if there is a data point which has value X equal
  663. /// to valuePosition, and returns label from data point if such value exist.
  664. /// If data point with this value not exists empty string will be returned.
  665. /// If all data points have X value zero, index is used instead of X value.
  666. /// </summary>
  667. /// <param name="series">Data series</param>
  668. /// <param name="valuePosition">A value which should be found in data points x values</param>
  669. /// <param name="nonZeroXValues">Series X values are not zeros.</param>
  670. /// <param name="indexedSeries">Series is indexed. All X values are zeros or IsXValueIndexed flag set.</param>
  671. /// <returns>LabelStyle</returns>
  672. private string GetPointLabel(
  673. List<string> series,
  674. double valuePosition,
  675. bool nonZeroXValues,
  676. bool indexedSeries
  677. )
  678. {
  679. // Get max number of data points in the series
  680. int maxPointCount = 0;
  681. foreach (string seriesName in series)
  682. {
  683. Series ser = Common.DataManager.Series[seriesName];
  684. maxPointCount = Math.Max(maxPointCount, ser.Points.Count);
  685. }
  686. // Check if axis only contains axis abels
  687. bool allEmpty = true;
  688. foreach( string seriesName in series )
  689. {
  690. // Get series by name
  691. Series ser = Common.DataManager.Series[ seriesName ];
  692. // Check if series has axis labels set
  693. if ((axisType == AxisName.X || axisType == AxisName.X2) && (margin != 0 || maxPointCount == 1 || !this._autoMinimum) && !ser.IsXValueIndexed)
  694. {
  695. if( ser.Points[ 0 ].AxisLabel.Length > 0 && ser.Points[ ser.Points.Count - 1 ].AxisLabel.Length > 0 )
  696. {
  697. allEmpty = false;
  698. }
  699. }
  700. // Try getting label from the point
  701. if(!ser.noLabelsInPoints || (nonZeroXValues && indexedSeries))
  702. {
  703. string result = GetPointLabel( ser, valuePosition, nonZeroXValues, indexedSeries );
  704. if(!String.IsNullOrEmpty(result))
  705. {
  706. return result;
  707. }
  708. }
  709. // VSTS 140676: Serach for IndexedSeriesLabelsSourceAttr attribute
  710. // to find if we have indexed series as source of formula generated nonindexed series.
  711. String labelSeriesName = ser[DataFormula.IndexedSeriesLabelsSourceAttr];
  712. if (!String.IsNullOrEmpty(labelSeriesName))
  713. {
  714. Series labelsSeries = Common.DataManager.Series[labelSeriesName];
  715. if (labelsSeries != null)
  716. {
  717. string result = GetPointLabel(labelsSeries, valuePosition, nonZeroXValues, true);
  718. if (!String.IsNullOrEmpty(result))
  719. {
  720. return result;
  721. }
  722. }
  723. }
  724. }
  725. if( !allEmpty )
  726. {
  727. return " ";
  728. }
  729. else
  730. {
  731. return "";
  732. }
  733. }
  734. /// <summary>
  735. /// This method checks if there is a data point which has value X equal
  736. /// to valuePosition, and returns label from data point if such value exist.
  737. /// If data point with this value not exists empty string will be returned.
  738. /// If all data points have X value zero, index is used instead of X value.
  739. /// </summary>
  740. /// <param name="series">Data series</param>
  741. /// <param name="valuePosition">A value which should be found in data points x values</param>
  742. /// <param name="nonZeroXValues">Series X values are not zeros.</param>
  743. /// <param name="indexedSeries">Series is indexed. All X values are zeros or IsXValueIndexed flag set.</param>
  744. /// <returns>LabelStyle</returns>
  745. private string GetPointLabel(
  746. Series series,
  747. double valuePosition,
  748. bool nonZeroXValues,
  749. bool indexedSeries)
  750. {
  751. int pointIndx = 1;
  752. if( axisType == AxisName.Y || axisType == AxisName.Y2 )
  753. {
  754. return "";
  755. }
  756. if( !(( axisType == AxisName.X && series.XAxisType == AxisType.Primary ) || ( axisType == AxisName.X2 && series.XAxisType == AxisType.Secondary )) )
  757. {
  758. #if SUBAXES
  759. if(series.XSubAxisName != ((Axis)this).SubAxisName)
  760. {
  761. return "";
  762. }
  763. #endif // SUBAXES
  764. return "";
  765. }
  766. // Loop through all series data points
  767. foreach( DataPoint point in series.Points )
  768. {
  769. // If series is indexed (all X values are zeros or IsXValueIndexed flag set)
  770. if( indexedSeries )
  771. {
  772. // If axis label position matches point index
  773. if( valuePosition == pointIndx )
  774. {
  775. // Use X value if axis label is not set and X values in series are not zeros
  776. if(point.AxisLabel.Length == 0 && nonZeroXValues)
  777. {
  778. return ValueConverter.FormatValue(
  779. this.Common.Chart,
  780. this,
  781. null,
  782. point.XValue,
  783. this.LabelStyle.Format,
  784. series.XValueType,
  785. ChartElementType.AxisLabels);
  786. }
  787. // Return axis label from data point
  788. return point.ReplaceKeywords(point.AxisLabel);
  789. }
  790. }
  791. else
  792. {
  793. // Find x value using Data point X values
  794. if( point.XValue == valuePosition )
  795. {
  796. // Return label
  797. return point.ReplaceKeywords(point.AxisLabel);
  798. }
  799. }
  800. pointIndx++;
  801. }
  802. return "";
  803. }
  804. #endregion
  805. }
  806. }