Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

Chart.cs 18KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570
  1. /*************************************************************************************
  2. DocX – DocX is the community edition of Xceed Words for .NET
  3. Copyright (C) 2009-2016 Xceed Software Inc.
  4. This program is provided to you under the terms of the Microsoft Public
  5. License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
  6. For more features and fast professional support,
  7. pick up Xceed Words for .NET at https://xceed.com/xceed-words-for-net/
  8. ***********************************************************************************/
  9. using System;
  10. using System.Collections.Generic;
  11. using System.Linq;
  12. using System.Xml.Linq;
  13. using System.Collections;
  14. using System.Drawing;
  15. using System.Globalization;
  16. namespace Xceed.Words.NET
  17. {
  18. /// <summary>
  19. /// Represents every Chart in this document.
  20. /// </summary>
  21. public abstract class Chart
  22. {
  23. #region Public Properties
  24. /// <summary>
  25. /// The xml representation of this chart
  26. /// </summary>
  27. public XDocument Xml
  28. {
  29. get; private set;
  30. }
  31. /// <summary>
  32. /// Chart's series
  33. /// </summary>
  34. public List<Series> Series
  35. {
  36. get
  37. {
  38. var series = new List<Series>();
  39. var ser = XName.Get( "ser", DocX.c.NamespaceName );
  40. int index = 1;
  41. foreach( var element in ChartXml.Elements( ser ) )
  42. {
  43. element.Add( new XElement( XName.Get("idx", DocX.c.NamespaceName ) ), index.ToString() );
  44. series.Add( new Series( element ) );
  45. ++index;
  46. }
  47. return series;
  48. }
  49. }
  50. /// <summary>
  51. /// Return maximum count of series
  52. /// </summary>
  53. public virtual Int16 MaxSeriesCount
  54. {
  55. get
  56. {
  57. return Int16.MaxValue;
  58. }
  59. }
  60. /// <summary>
  61. /// Chart's legend.
  62. /// If legend doesn't exist property is null.
  63. /// </summary>
  64. public ChartLegend Legend
  65. {
  66. get; private set;
  67. }
  68. /// <summary>
  69. /// Represents the category axis
  70. /// </summary>
  71. public CategoryAxis CategoryAxis
  72. {
  73. get; private set;
  74. }
  75. /// <summary>
  76. /// Represents the values axis
  77. /// </summary>
  78. public ValueAxis ValueAxis
  79. {
  80. get; private set;
  81. }
  82. /// <summary>
  83. /// Represents existing the axis
  84. /// </summary>
  85. public virtual Boolean IsAxisExist
  86. {
  87. get
  88. {
  89. return true;
  90. }
  91. }
  92. /// <summary>
  93. /// Get or set 3D view for this chart
  94. /// </summary>
  95. public Boolean View3D
  96. {
  97. get
  98. {
  99. return ChartXml.Name.LocalName.Contains( "3D" );
  100. }
  101. set
  102. {
  103. if( value )
  104. {
  105. if( !View3D )
  106. {
  107. String currentName = ChartXml.Name.LocalName;
  108. ChartXml.Name = XName.Get( currentName.Replace( "Chart", "3DChart" ), DocX.c.NamespaceName );
  109. }
  110. }
  111. else
  112. {
  113. if( View3D )
  114. {
  115. String currentName = ChartXml.Name.LocalName;
  116. ChartXml.Name = XName.Get( currentName.Replace( "3DChart", "Chart" ), DocX.c.NamespaceName );
  117. }
  118. }
  119. }
  120. }
  121. /// <summary>
  122. /// Specifies how blank cells shall be plotted on a chart
  123. /// </summary>
  124. public DisplayBlanksAs DisplayBlanksAs
  125. {
  126. get
  127. {
  128. return XElementHelpers.GetValueToEnum<DisplayBlanksAs>(
  129. ChartRootXml.Element( XName.Get( "dispBlanksAs", DocX.c.NamespaceName ) ) );
  130. }
  131. set
  132. {
  133. XElementHelpers.SetValueFromEnum<DisplayBlanksAs>(
  134. ChartRootXml.Element( XName.Get( "dispBlanksAs", DocX.c.NamespaceName ) ), value );
  135. }
  136. }
  137. #endregion
  138. #region Protected Properties
  139. protected XElement ChartXml
  140. {
  141. get; private set;
  142. }
  143. protected XElement ChartRootXml
  144. {
  145. get; private set;
  146. }
  147. #endregion
  148. #region Constructors
  149. /// <summary>
  150. /// Create an Chart for this document
  151. /// </summary>
  152. public Chart()
  153. {
  154. // Create global xml
  155. this.Xml = XDocument.Parse
  156. ( @"<?xml version=""1.0"" encoding=""UTF-8"" standalone=""yes""?>
  157. <c:chartSpace xmlns:c=""http://schemas.openxmlformats.org/drawingml/2006/chart"" xmlns:a=""http://schemas.openxmlformats.org/drawingml/2006/main"" xmlns:r=""http://schemas.openxmlformats.org/officeDocument/2006/relationships"">
  158. <c:roundedCorners val=""0""/>
  159. <c:chart>
  160. <c:autoTitleDeleted val=""0""/>
  161. <c:plotVisOnly val=""1""/>
  162. <c:dispBlanksAs val=""gap""/>
  163. <c:showDLblsOverMax val=""0""/>
  164. </c:chart>
  165. </c:chartSpace>" );
  166. // Create a real chart xml in an inheritor
  167. this.ChartXml = this.CreateChartXml();
  168. // Create result plotarea element
  169. var plotAreaXml = new XElement( XName.Get( "plotArea", DocX.c.NamespaceName ),
  170. new XElement( XName.Get( "layout", DocX.c.NamespaceName ) ),
  171. this.ChartXml );
  172. // Set labels
  173. var dLblsXml = XElement.Parse(
  174. @"<c:dLbls xmlns:c=""http://schemas.openxmlformats.org/drawingml/2006/chart"">
  175. <c:showLegendKey val=""0""/>
  176. <c:showVal val=""0""/>
  177. <c:showCatName val=""0""/>
  178. <c:showSerName val=""0""/>
  179. <c:showPercent val=""0""/>
  180. <c:showBubbleSize val=""0""/>
  181. <c:showLeaderLines val=""1""/>
  182. </c:dLbls>" );
  183. this.ChartXml.Add( dLblsXml );
  184. // if axes exists, create their
  185. if( this.IsAxisExist )
  186. {
  187. this.CategoryAxis = new CategoryAxis( "148921728" );
  188. this.ValueAxis = new ValueAxis( "154227840" );
  189. var axIDcatXml = XElement.Parse( String.Format( @"<c:axId val=""{0}"" xmlns:c=""http://schemas.openxmlformats.org/drawingml/2006/chart""/>", this.CategoryAxis.Id ) );
  190. var axIDvalXml = XElement.Parse( String.Format( @"<c:axId val=""{0}"" xmlns:c=""http://schemas.openxmlformats.org/drawingml/2006/chart""/>", this.ValueAxis.Id ) );
  191. var gapWidth = this.ChartXml.Element( XName.Get( "gapWidth", DocX.c.NamespaceName ) );
  192. if( gapWidth != null )
  193. {
  194. gapWidth.AddAfterSelf( axIDvalXml );
  195. gapWidth.AddAfterSelf( axIDcatXml );
  196. }
  197. else
  198. {
  199. this.ChartXml.Add( axIDcatXml );
  200. this.ChartXml.Add( axIDvalXml );
  201. }
  202. plotAreaXml.Add( this.CategoryAxis.Xml );
  203. plotAreaXml.Add( this.ValueAxis.Xml );
  204. }
  205. this.ChartRootXml = this.Xml.Root.Element( XName.Get( "chart", DocX.c.NamespaceName ) );
  206. this.ChartRootXml.Element( XName.Get( "autoTitleDeleted", DocX.c.NamespaceName ) ).AddAfterSelf( plotAreaXml );
  207. }
  208. #endregion
  209. #region Public Methods
  210. /// <summary>
  211. /// Add a new series to this chart
  212. /// </summary>
  213. public void AddSeries( Series series )
  214. {
  215. var seriesCount = ChartXml.Elements( XName.Get( "ser", DocX.c.NamespaceName ) ).Count();
  216. if( seriesCount >= MaxSeriesCount )
  217. throw new InvalidOperationException( "Maximum series for this chart is" + MaxSeriesCount.ToString() + "and have exceeded!" );
  218. //To work in Words, all series need an Index and Order.
  219. var value = seriesCount + 1;
  220. var content = new XAttribute( XName.Get( "val" ), value.ToString() );
  221. series.Xml.AddFirst( new XElement( XName.Get( "order", DocX.c.NamespaceName ), content ) );
  222. series.Xml.AddFirst( new XElement( XName.Get( "idx", DocX.c.NamespaceName ), content ) );
  223. this.ChartXml.Add( series.Xml );
  224. }
  225. /// <summary>
  226. /// Add standart legend to the chart.
  227. /// </summary>
  228. public void AddLegend()
  229. {
  230. AddLegend( ChartLegendPosition.Right, false );
  231. }
  232. /// <summary>
  233. /// Add a legend with parameters to the chart.
  234. /// </summary>
  235. public void AddLegend( ChartLegendPosition position, Boolean overlay )
  236. {
  237. if( Legend != null )
  238. {
  239. this.RemoveLegend();
  240. }
  241. this.Legend = new ChartLegend( position, overlay );
  242. this.ChartRootXml.Element( XName.Get( "plotArea", DocX.c.NamespaceName ) ).AddAfterSelf( Legend.Xml );
  243. }
  244. /// <summary>
  245. /// Remove the legend from the chart.
  246. /// </summary>
  247. public void RemoveLegend()
  248. {
  249. Legend.Xml.Remove();
  250. Legend = null;
  251. }
  252. #endregion
  253. #region Protected Methods
  254. /// <summary>
  255. /// An abstract method which creates the current chart xml
  256. /// </summary>
  257. protected abstract XElement CreateChartXml();
  258. #endregion
  259. }
  260. /// <summary>
  261. /// Represents a chart series
  262. /// </summary>
  263. public class Series
  264. {
  265. #region Private Members
  266. private XElement _strCache;
  267. private XElement _numCache;
  268. #endregion
  269. #region Public Properties
  270. public Color Color
  271. {
  272. get
  273. {
  274. var colorElement = Xml.Element( XName.Get( "spPr", DocX.c.NamespaceName ) );
  275. if( colorElement == null )
  276. return Color.Transparent;
  277. else
  278. {
  279. var val = colorElement.Element( XName.Get( "solidFill", DocX.a.NamespaceName ) )
  280. .Element( XName.Get( "srgbClr", DocX.a.NamespaceName ) )
  281. .Attribute( XName.Get( "val" ) )
  282. .Value;
  283. return Color.FromArgb( Int32.Parse( val, NumberStyles.HexNumber ) );
  284. }
  285. }
  286. set
  287. {
  288. var colorElement = this.Xml.Element( XName.Get( "spPr", DocX.c.NamespaceName ) );
  289. if( colorElement != null )
  290. {
  291. colorElement.Remove();
  292. }
  293. colorElement = new XElement( XName.Get( "spPr", DocX.c.NamespaceName ),
  294. new XElement( XName.Get( "solidFill", DocX.a.NamespaceName ),
  295. new XElement( XName.Get( "srgbClr", DocX.a.NamespaceName ), new XAttribute( XName.Get( "val" ), value.ToHex() ) ) ) );
  296. this.Xml.Element( XName.Get( "tx", DocX.c.NamespaceName ) ).AddAfterSelf( colorElement );
  297. }
  298. }
  299. #endregion
  300. #region Internal Properties
  301. /// <summary>
  302. /// Series xml element
  303. /// </summary>
  304. internal XElement Xml
  305. {
  306. get; private set;
  307. }
  308. #endregion
  309. #region Constructors
  310. internal Series( XElement xml )
  311. {
  312. Xml = xml;
  313. _strCache = xml.Element( XName.Get( "cat", DocX.c.NamespaceName ) ).Element( XName.Get( "strRef", DocX.c.NamespaceName ) ).Element( XName.Get( "strCache", DocX.c.NamespaceName ) );
  314. _numCache = xml.Element( XName.Get( "val", DocX.c.NamespaceName ) ).Element( XName.Get( "numRef", DocX.c.NamespaceName ) ).Element( XName.Get( "numCache", DocX.c.NamespaceName ) );
  315. }
  316. public Series( String name )
  317. {
  318. _strCache = new XElement( XName.Get( "strCache", DocX.c.NamespaceName ) );
  319. _numCache = new XElement( XName.Get( "numCache", DocX.c.NamespaceName ) );
  320. this.Xml = new XElement( XName.Get( "ser", DocX.c.NamespaceName ),
  321. new XElement( XName.Get( "tx", DocX.c.NamespaceName ),
  322. new XElement( XName.Get( "strRef", DocX.c.NamespaceName ),
  323. new XElement( XName.Get( "f", DocX.c.NamespaceName ), "" ),
  324. new XElement( XName.Get( "strCache", DocX.c.NamespaceName ),
  325. new XElement( XName.Get( "pt", DocX.c.NamespaceName ),
  326. new XAttribute( XName.Get( "idx" ), "0" ),
  327. new XElement( XName.Get( "v", DocX.c.NamespaceName ), name ) ) ) ) ),
  328. new XElement( XName.Get( "invertIfNegative", DocX.c.NamespaceName ), "0" ),
  329. new XElement( XName.Get( "cat", DocX.c.NamespaceName ),
  330. new XElement( XName.Get( "strRef", DocX.c.NamespaceName ),
  331. new XElement( XName.Get( "f", DocX.c.NamespaceName ), "" ),
  332. _strCache ) ),
  333. new XElement( XName.Get( "val", DocX.c.NamespaceName ),
  334. new XElement( XName.Get( "numRef", DocX.c.NamespaceName ),
  335. new XElement( XName.Get( "f", DocX.c.NamespaceName ), "" ),
  336. _numCache ) )
  337. );
  338. }
  339. #endregion
  340. #region Public Methods
  341. public void Bind( ICollection list, String categoryPropertyName, String valuePropertyName )
  342. {
  343. var ptCount = new XElement( XName.Get( "ptCount", DocX.c.NamespaceName ), new XAttribute( XName.Get( "val" ), list.Count ) );
  344. var formatCode = new XElement( XName.Get( "formatCode", DocX.c.NamespaceName ), "General" );
  345. _strCache.RemoveAll();
  346. _numCache.RemoveAll();
  347. _strCache.Add( ptCount );
  348. _numCache.Add( formatCode );
  349. _numCache.Add( ptCount );
  350. Int32 index = 0;
  351. XElement pt;
  352. foreach( var item in list )
  353. {
  354. pt = new XElement( XName.Get( "pt", DocX.c.NamespaceName ), new XAttribute( XName.Get( "idx" ), index ),
  355. new XElement( XName.Get( "v", DocX.c.NamespaceName ), item.GetType().GetProperty( categoryPropertyName ).GetValue( item, null ) ) );
  356. _strCache.Add( pt );
  357. pt = new XElement( XName.Get( "pt", DocX.c.NamespaceName ), new XAttribute( XName.Get( "idx" ), index ),
  358. new XElement( XName.Get( "v", DocX.c.NamespaceName ), item.GetType().GetProperty( valuePropertyName ).GetValue( item, null ) ) );
  359. _numCache.Add( pt );
  360. index++;
  361. }
  362. }
  363. public void Bind( IList categories, IList values )
  364. {
  365. if( categories.Count != values.Count )
  366. throw new ArgumentException( "Categories count must equal to Values count" );
  367. var ptCount = new XElement( XName.Get( "ptCount", DocX.c.NamespaceName ), new XAttribute( XName.Get( "val" ), categories.Count ) );
  368. var formatCode = new XElement( XName.Get( "formatCode", DocX.c.NamespaceName ), "General" );
  369. _strCache.RemoveAll();
  370. _numCache.RemoveAll();
  371. _strCache.Add( ptCount );
  372. _numCache.Add( formatCode );
  373. _numCache.Add( ptCount );
  374. XElement pt;
  375. for( int index = 0; index < categories.Count; index++ )
  376. {
  377. pt = new XElement( XName.Get( "pt", DocX.c.NamespaceName ), new XAttribute( XName.Get( "idx" ), index ),
  378. new XElement( XName.Get( "v", DocX.c.NamespaceName ), categories[ index ].ToString() ) );
  379. _strCache.Add( pt );
  380. pt = new XElement( XName.Get( "pt", DocX.c.NamespaceName ), new XAttribute( XName.Get( "idx" ), index ),
  381. new XElement( XName.Get( "v", DocX.c.NamespaceName ), values[ index ].ToString() ) );
  382. _numCache.Add( pt );
  383. }
  384. }
  385. #endregion
  386. }
  387. /// <summary>
  388. /// Represents a chart legend
  389. /// More: http://msdn.microsoft.com/ru-ru/library/cc845123.aspx
  390. /// </summary>
  391. public class ChartLegend
  392. {
  393. #region Public Properties
  394. /// <summary>
  395. /// Specifies that other chart elements shall be allowed to overlap this chart element
  396. /// </summary>
  397. public Boolean Overlay
  398. {
  399. get
  400. {
  401. return Xml.Element( XName.Get( "overlay", DocX.c.NamespaceName ) ).Attribute( "val" ).Value == "1";
  402. }
  403. set
  404. {
  405. Xml.Element( XName.Get( "overlay", DocX.c.NamespaceName ) ).Attribute( "val" ).Value = GetOverlayValue( value );
  406. }
  407. }
  408. /// <summary>
  409. /// Specifies the possible positions for a legend
  410. /// </summary>
  411. public ChartLegendPosition Position
  412. {
  413. get
  414. {
  415. return XElementHelpers.GetValueToEnum<ChartLegendPosition>(
  416. Xml.Element( XName.Get( "legendPos", DocX.c.NamespaceName ) ) );
  417. }
  418. set
  419. {
  420. XElementHelpers.SetValueFromEnum<ChartLegendPosition>(
  421. Xml.Element( XName.Get( "legendPos", DocX.c.NamespaceName ) ), value );
  422. }
  423. }
  424. #endregion
  425. #region Internal Properties
  426. /// <summary>
  427. /// Legend xml element
  428. /// </summary>
  429. internal XElement Xml
  430. {
  431. get; private set;
  432. }
  433. #endregion
  434. #region Constructors
  435. internal ChartLegend( ChartLegendPosition position, Boolean overlay )
  436. {
  437. Xml = new XElement(
  438. XName.Get( "legend", DocX.c.NamespaceName ),
  439. new XElement( XName.Get( "legendPos", DocX.c.NamespaceName ), new XAttribute( "val", XElementHelpers.GetXmlNameFromEnum<ChartLegendPosition>( position ) ) ),
  440. new XElement( XName.Get( "overlay", DocX.c.NamespaceName ), new XAttribute( "val", GetOverlayValue( overlay ) ) )
  441. );
  442. }
  443. #endregion
  444. #region Private Methods
  445. /// <summary>
  446. /// ECMA-376, page 3840
  447. /// 21.2.2.132 overlay (Overlay)
  448. /// </summary>
  449. private String GetOverlayValue( Boolean overlay )
  450. {
  451. if( overlay )
  452. return "1";
  453. else
  454. return "0";
  455. }
  456. #endregion
  457. }
  458. /// <summary>
  459. /// Specifies the possible positions for a legend.
  460. /// 21.2.3.24 ST_LegendPos (Legend Position)
  461. /// </summary>
  462. public enum ChartLegendPosition
  463. {
  464. [XmlName( "t" )]
  465. Top,
  466. [XmlName( "b" )]
  467. Bottom,
  468. [XmlName( "l" )]
  469. Left,
  470. [XmlName( "r" )]
  471. Right,
  472. [XmlName( "tr" )]
  473. TopRight
  474. }
  475. /// <summary>
  476. /// Specifies the possible ways to display blanks.
  477. /// 21.2.3.10 ST_DispBlanksAs (Display Blanks As)
  478. /// </summary>
  479. public enum DisplayBlanksAs
  480. {
  481. [XmlName( "gap" )]
  482. Gap,
  483. [XmlName( "span" )]
  484. Span,
  485. [XmlName( "zero" )]
  486. Zero
  487. }
  488. }