You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614
  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. public static XName CXName(string eleName)
  260. {
  261. return XName.Get(eleName, DocX.c.NamespaceName);
  262. }
  263. public static XElement CXElement(string eleName, string value, string valName = "val")
  264. {
  265. return new XElement(CXName(eleName), new XAttribute(CXName(valName), value));
  266. }
  267. public static XElement CElement(string eleName,params object[] eles)
  268. {
  269. return new XElement(CXName(eleName), eles);
  270. }
  271. public static XAttribute CAttr(string arr, string value)
  272. {
  273. return new XAttribute(CXName(arr), value);
  274. }
  275. }
  276. /// <summary>
  277. /// Represents a chart series
  278. /// </summary>
  279. public class Series
  280. {
  281. #region Private Members
  282. private XElement _strCache;
  283. private XElement _numCache;
  284. #endregion
  285. #region Public Properties
  286. public Color Color
  287. {
  288. get
  289. {
  290. var colorElement = Xml.Element(XName.Get("spPr", DocX.c.NamespaceName));
  291. if (colorElement == null)
  292. return Color.Transparent;
  293. else
  294. {
  295. var val = colorElement.Element(XName.Get("solidFill", DocX.a.NamespaceName))
  296. .Element(XName.Get("srgbClr", DocX.a.NamespaceName))
  297. .Attribute(XName.Get("val"))
  298. .Value;
  299. return Color.FromArgb(Int32.Parse(val, NumberStyles.HexNumber));
  300. }
  301. }
  302. set
  303. {
  304. var colorElement = this.Xml.Element(XName.Get("spPr", DocX.c.NamespaceName));
  305. if (colorElement != null)
  306. {
  307. colorElement.Remove();
  308. }
  309. colorElement = new XElement(XName.Get("spPr", DocX.c.NamespaceName),
  310. new XElement(XName.Get("solidFill", DocX.a.NamespaceName),
  311. new XElement(XName.Get("srgbClr", DocX.a.NamespaceName), new XAttribute(XName.Get("val"), value.ToHex()))));
  312. this.Xml.Element(XName.Get("tx", DocX.c.NamespaceName)).AddAfterSelf(colorElement);
  313. }
  314. }
  315. public bool ShowValue
  316. {
  317. set
  318. {
  319. var sve = this.Xml.Element(XName.Get("dLbls", DocX.c.NamespaceName));
  320. if (sve != null)
  321. {
  322. }
  323. var list = new List<XElement>();
  324. list.Add(Chart.CXElement("dLblPos", "outEnd"));//< c:dLblPos val = "outEnd" />
  325. list.Add(Chart.CXElement("showLegendKey", "0"));//< c:showLegendKey val = "0" />
  326. list.Add(Chart.CXElement("showVal", Convert.ToInt32(value).ToString()));// < c:showVal val = "1" />
  327. list.Add(Chart.CXElement("showCatName", "0"));//< c:showCatName val = "0" />
  328. list.Add(Chart.CXElement("showSerName", "0"));//< c:showSerName val = "0" />
  329. list.Add(Chart.CXElement("showPercent", "0"));//< c:showPercent val = "0" />
  330. list.Add(Chart.CXElement("showBubbleSize", "0"));//< c:showBubbleSize val = "0" />
  331. list.Add(Chart.CXElement("showLeaderLines", "0"));//< c:showLeaderLines val = "0" />
  332. sve = new XElement(Chart.CXName("dLbls"), list.ToArray());
  333. this.Xml.Element(Chart.CXName("invertIfNegative")).AddAfterSelf(sve);
  334. }
  335. }
  336. #endregion
  337. #region Internal Properties
  338. /// <summary>
  339. /// Series xml element
  340. /// </summary>
  341. internal XElement Xml
  342. {
  343. get; private set;
  344. }
  345. #endregion
  346. #region Constructors
  347. internal Series(XElement xml)
  348. {
  349. Xml = xml;
  350. _strCache = xml.Element(XName.Get("cat", DocX.c.NamespaceName)).Element(XName.Get("strRef", DocX.c.NamespaceName)).Element(XName.Get("strCache", DocX.c.NamespaceName));
  351. _numCache = xml.Element(XName.Get("val", DocX.c.NamespaceName)).Element(XName.Get("numRef", DocX.c.NamespaceName)).Element(XName.Get("numCache", DocX.c.NamespaceName));
  352. }
  353. public Series(String name)
  354. {
  355. _strCache = new XElement(XName.Get("strCache", DocX.c.NamespaceName));
  356. _numCache = new XElement(XName.Get("numCache", DocX.c.NamespaceName));
  357. this.Xml = new XElement(XName.Get("ser", DocX.c.NamespaceName),
  358. new XElement(XName.Get("tx", DocX.c.NamespaceName),
  359. new XElement(XName.Get("strRef", DocX.c.NamespaceName),
  360. new XElement(XName.Get("f", DocX.c.NamespaceName), ""),
  361. new XElement(XName.Get("strCache", DocX.c.NamespaceName),
  362. new XElement(XName.Get("pt", DocX.c.NamespaceName),
  363. new XAttribute(XName.Get("idx"), "0"),
  364. new XElement(XName.Get("v", DocX.c.NamespaceName), name))))),
  365. new XElement(XName.Get("invertIfNegative", DocX.c.NamespaceName), "0"),
  366. new XElement(XName.Get("cat", DocX.c.NamespaceName),
  367. new XElement(XName.Get("strRef", DocX.c.NamespaceName),
  368. new XElement(XName.Get("f", DocX.c.NamespaceName), ""),
  369. _strCache)),
  370. new XElement(XName.Get("val", DocX.c.NamespaceName),
  371. new XElement(XName.Get("numRef", DocX.c.NamespaceName),
  372. new XElement(XName.Get("f", DocX.c.NamespaceName), ""),
  373. _numCache))
  374. );
  375. }
  376. #endregion
  377. #region Public Methods
  378. public void Bind(ICollection list, String categoryPropertyName, String valuePropertyName)
  379. {
  380. var ptCount = new XElement(XName.Get("ptCount", DocX.c.NamespaceName), new XAttribute(XName.Get("val"), list.Count));
  381. var formatCode = new XElement(XName.Get("formatCode", DocX.c.NamespaceName), "General");
  382. _strCache.RemoveAll();
  383. _numCache.RemoveAll();
  384. _strCache.Add(ptCount);
  385. _numCache.Add(formatCode);
  386. _numCache.Add(ptCount);
  387. Int32 index = 0;
  388. XElement pt;
  389. foreach (var item in list)
  390. {
  391. pt = new XElement(XName.Get("pt", DocX.c.NamespaceName), new XAttribute(XName.Get("idx"), index),
  392. new XElement(XName.Get("v", DocX.c.NamespaceName), item.GetType().GetProperty(categoryPropertyName).GetValue(item, null)));
  393. _strCache.Add(pt);
  394. pt = new XElement(XName.Get("pt", DocX.c.NamespaceName), new XAttribute(XName.Get("idx"), index),
  395. new XElement(XName.Get("v", DocX.c.NamespaceName), item.GetType().GetProperty(valuePropertyName).GetValue(item, null)));
  396. _numCache.Add(pt);
  397. index++;
  398. }
  399. }
  400. public void Bind(IList categories, IList values)
  401. {
  402. if (categories.Count != values.Count)
  403. throw new ArgumentException("Categories count must equal to Values count");
  404. var ptCount = new XElement(XName.Get("ptCount", DocX.c.NamespaceName), new XAttribute(XName.Get("val"), categories.Count));
  405. var formatCode = new XElement(XName.Get("formatCode", DocX.c.NamespaceName), "General");
  406. _strCache.RemoveAll();
  407. _numCache.RemoveAll();
  408. _strCache.Add(ptCount);
  409. _numCache.Add(formatCode);
  410. _numCache.Add(ptCount);
  411. XElement pt;
  412. for (int index = 0; index < categories.Count; index++)
  413. {
  414. pt = new XElement(XName.Get("pt", DocX.c.NamespaceName), new XAttribute(XName.Get("idx"), index),
  415. new XElement(XName.Get("v", DocX.c.NamespaceName), categories[index].ToString()));
  416. _strCache.Add(pt);
  417. pt = new XElement(XName.Get("pt", DocX.c.NamespaceName), new XAttribute(XName.Get("idx"), index),
  418. new XElement(XName.Get("v", DocX.c.NamespaceName), values[index].ToString()));
  419. _numCache.Add(pt);
  420. }
  421. }
  422. #endregion
  423. }
  424. /// <summary>
  425. /// Represents a chart legend
  426. /// More: http://msdn.microsoft.com/ru-ru/library/cc845123.aspx
  427. /// </summary>
  428. public class ChartLegend
  429. {
  430. #region Public Properties
  431. /// <summary>
  432. /// Specifies that other chart elements shall be allowed to overlap this chart element
  433. /// </summary>
  434. public Boolean Overlay
  435. {
  436. get
  437. {
  438. return Xml.Element(XName.Get("overlay", DocX.c.NamespaceName)).Attribute("val").Value == "1";
  439. }
  440. set
  441. {
  442. Xml.Element(XName.Get("overlay", DocX.c.NamespaceName)).Attribute("val").Value = GetOverlayValue(value);
  443. }
  444. }
  445. /// <summary>
  446. /// Specifies the possible positions for a legend
  447. /// </summary>
  448. public ChartLegendPosition Position
  449. {
  450. get
  451. {
  452. return XElementHelpers.GetValueToEnum<ChartLegendPosition>(
  453. Xml.Element(XName.Get("legendPos", DocX.c.NamespaceName)));
  454. }
  455. set
  456. {
  457. XElementHelpers.SetValueFromEnum<ChartLegendPosition>(
  458. Xml.Element(XName.Get("legendPos", DocX.c.NamespaceName)), value);
  459. }
  460. }
  461. #endregion
  462. #region Internal Properties
  463. /// <summary>
  464. /// Legend xml element
  465. /// </summary>
  466. internal XElement Xml
  467. {
  468. get; private set;
  469. }
  470. #endregion
  471. #region Constructors
  472. internal ChartLegend(ChartLegendPosition position, Boolean overlay)
  473. {
  474. Xml = new XElement(
  475. XName.Get("legend", DocX.c.NamespaceName),
  476. new XElement(XName.Get("legendPos", DocX.c.NamespaceName), new XAttribute("val", XElementHelpers.GetXmlNameFromEnum<ChartLegendPosition>(position))),
  477. new XElement(XName.Get("overlay", DocX.c.NamespaceName), new XAttribute("val", GetOverlayValue(overlay)))
  478. );
  479. }
  480. #endregion
  481. #region Private Methods
  482. /// <summary>
  483. /// ECMA-376, page 3840
  484. /// 21.2.2.132 overlay (Overlay)
  485. /// </summary>
  486. private String GetOverlayValue(Boolean overlay)
  487. {
  488. if (overlay)
  489. return "1";
  490. else
  491. return "0";
  492. }
  493. #endregion
  494. }
  495. /// <summary>
  496. /// Specifies the possible positions for a legend.
  497. /// 21.2.3.24 ST_LegendPos (Legend Position)
  498. /// </summary>
  499. public enum ChartLegendPosition
  500. {
  501. [XmlName("t")]
  502. Top,
  503. [XmlName("b")]
  504. Bottom,
  505. [XmlName("l")]
  506. Left,
  507. [XmlName("r")]
  508. Right,
  509. [XmlName("tr")]
  510. TopRight
  511. }
  512. /// <summary>
  513. /// Specifies the possible ways to display blanks.
  514. /// 21.2.3.10 ST_DispBlanksAs (Display Blanks As)
  515. /// </summary>
  516. public enum DisplayBlanksAs
  517. {
  518. [XmlName("gap")]
  519. Gap,
  520. [XmlName("span")]
  521. Span,
  522. [XmlName("zero")]
  523. Zero
  524. }
  525. }