/************************************************************************************* DocX – DocX is the community edition of Xceed Words for .NET Copyright (C) 2009-2016 Xceed Software Inc. This program is provided to you under the terms of the Microsoft Public License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license For more features and fast professional support, pick up Xceed Words for .NET at https://xceed.com/xceed-words-for-net/ ***********************************************************************************/ using System; using System.Collections.Generic; using System.Linq; using System.Xml.Linq; using System.Collections; using System.Drawing; using System.Globalization; namespace Xceed.Words.NET { /// /// Represents every Chart in this document. /// public abstract class Chart { #region Public Properties /// /// The xml representation of this chart /// public XDocument Xml { get; private set; } /// /// Chart's series /// public List Series { get { var series = new List(); var ser = XName.Get( "ser", DocX.c.NamespaceName ); int index = 1; foreach( var element in ChartXml.Elements( ser ) ) { element.Add( new XElement( XName.Get("idx", DocX.c.NamespaceName ) ), index.ToString() ); series.Add( new Series( element ) ); ++index; } return series; } } /// /// Return maximum count of series /// public virtual Int16 MaxSeriesCount { get { return Int16.MaxValue; } } /// /// Chart's legend. /// If legend doesn't exist property is null. /// public ChartLegend Legend { get; private set; } /// /// Represents the category axis /// public CategoryAxis CategoryAxis { get; private set; } /// /// Represents the values axis /// public ValueAxis ValueAxis { get; private set; } /// /// Represents existing the axis /// public virtual Boolean IsAxisExist { get { return true; } } /// /// Get or set 3D view for this chart /// public Boolean View3D { get { return ChartXml.Name.LocalName.Contains( "3D" ); } set { if( value ) { if( !View3D ) { String currentName = ChartXml.Name.LocalName; ChartXml.Name = XName.Get( currentName.Replace( "Chart", "3DChart" ), DocX.c.NamespaceName ); } } else { if( View3D ) { String currentName = ChartXml.Name.LocalName; ChartXml.Name = XName.Get( currentName.Replace( "3DChart", "Chart" ), DocX.c.NamespaceName ); } } } } /// /// Specifies how blank cells shall be plotted on a chart /// public DisplayBlanksAs DisplayBlanksAs { get { return XElementHelpers.GetValueToEnum( ChartRootXml.Element( XName.Get( "dispBlanksAs", DocX.c.NamespaceName ) ) ); } set { XElementHelpers.SetValueFromEnum( ChartRootXml.Element( XName.Get( "dispBlanksAs", DocX.c.NamespaceName ) ), value ); } } #endregion #region Protected Properties protected XElement ChartXml { get; private set; } protected XElement ChartRootXml { get; private set; } #endregion #region Constructors /// /// Create an Chart for this document /// public Chart() { // Create global xml this.Xml = XDocument.Parse ( @" " ); // Create a real chart xml in an inheritor this.ChartXml = this.CreateChartXml(); // Create result plotarea element var plotAreaXml = new XElement( XName.Get( "plotArea", DocX.c.NamespaceName ), new XElement( XName.Get( "layout", DocX.c.NamespaceName ) ), this.ChartXml ); // Set labels var dLblsXml = XElement.Parse( @" " ); this.ChartXml.Add( dLblsXml ); // if axes exists, create their if( this.IsAxisExist ) { this.CategoryAxis = new CategoryAxis( "148921728" ); this.ValueAxis = new ValueAxis( "154227840" ); var axIDcatXml = XElement.Parse( String.Format( @"", this.CategoryAxis.Id ) ); var axIDvalXml = XElement.Parse( String.Format( @"", this.ValueAxis.Id ) ); var gapWidth = this.ChartXml.Element( XName.Get( "gapWidth", DocX.c.NamespaceName ) ); if( gapWidth != null ) { gapWidth.AddAfterSelf( axIDvalXml ); gapWidth.AddAfterSelf( axIDcatXml ); } else { this.ChartXml.Add( axIDcatXml ); this.ChartXml.Add( axIDvalXml ); } plotAreaXml.Add( this.CategoryAxis.Xml ); plotAreaXml.Add( this.ValueAxis.Xml ); } this.ChartRootXml = this.Xml.Root.Element( XName.Get( "chart", DocX.c.NamespaceName ) ); this.ChartRootXml.Element( XName.Get( "autoTitleDeleted", DocX.c.NamespaceName ) ).AddAfterSelf( plotAreaXml ); } #endregion #region Public Methods /// /// Add a new series to this chart /// public void AddSeries( Series series ) { var seriesCount = ChartXml.Elements( XName.Get( "ser", DocX.c.NamespaceName ) ).Count(); if( seriesCount >= MaxSeriesCount ) throw new InvalidOperationException( "Maximum series for this chart is" + MaxSeriesCount.ToString() + "and have exceeded!" ); //To work in Words, all series need an Index and Order. var value = seriesCount + 1; var content = new XAttribute( XName.Get( "val" ), value.ToString() ); series.Xml.AddFirst( new XElement( XName.Get( "order", DocX.c.NamespaceName ), content ) ); series.Xml.AddFirst( new XElement( XName.Get( "idx", DocX.c.NamespaceName ), content ) ); this.ChartXml.Add( series.Xml ); } /// /// Add standart legend to the chart. /// public void AddLegend() { AddLegend( ChartLegendPosition.Right, false ); } /// /// Add a legend with parameters to the chart. /// public void AddLegend( ChartLegendPosition position, Boolean overlay ) { if( Legend != null ) { this.RemoveLegend(); } this.Legend = new ChartLegend( position, overlay ); this.ChartRootXml.Element( XName.Get( "plotArea", DocX.c.NamespaceName ) ).AddAfterSelf( Legend.Xml ); } /// /// Remove the legend from the chart. /// public void RemoveLegend() { Legend.Xml.Remove(); Legend = null; } #endregion #region Protected Methods /// /// An abstract method which creates the current chart xml /// protected abstract XElement CreateChartXml(); #endregion } /// /// Represents a chart series /// public class Series { #region Private Members private XElement _strCache; private XElement _numCache; #endregion #region Public Properties public Color Color { get { var colorElement = Xml.Element( XName.Get( "spPr", DocX.c.NamespaceName ) ); if( colorElement == null ) return Color.Transparent; else { var val = colorElement.Element( XName.Get( "solidFill", DocX.a.NamespaceName ) ) .Element( XName.Get( "srgbClr", DocX.a.NamespaceName ) ) .Attribute( XName.Get( "val" ) ) .Value; return Color.FromArgb( Int32.Parse( val, NumberStyles.HexNumber ) ); } } set { var colorElement = this.Xml.Element( XName.Get( "spPr", DocX.c.NamespaceName ) ); if( colorElement != null ) { colorElement.Remove(); } colorElement = new XElement( XName.Get( "spPr", DocX.c.NamespaceName ), new XElement( XName.Get( "solidFill", DocX.a.NamespaceName ), new XElement( XName.Get( "srgbClr", DocX.a.NamespaceName ), new XAttribute( XName.Get( "val" ), value.ToHex() ) ) ) ); this.Xml.Element( XName.Get( "tx", DocX.c.NamespaceName ) ).AddAfterSelf( colorElement ); } } #endregion #region Internal Properties /// /// Series xml element /// internal XElement Xml { get; private set; } #endregion #region Constructors internal Series( XElement xml ) { Xml = xml; _strCache = xml.Element( XName.Get( "cat", DocX.c.NamespaceName ) ).Element( XName.Get( "strRef", DocX.c.NamespaceName ) ).Element( XName.Get( "strCache", DocX.c.NamespaceName ) ); _numCache = xml.Element( XName.Get( "val", DocX.c.NamespaceName ) ).Element( XName.Get( "numRef", DocX.c.NamespaceName ) ).Element( XName.Get( "numCache", DocX.c.NamespaceName ) ); } public Series( String name ) { _strCache = new XElement( XName.Get( "strCache", DocX.c.NamespaceName ) ); _numCache = new XElement( XName.Get( "numCache", DocX.c.NamespaceName ) ); this.Xml = new XElement( XName.Get( "ser", DocX.c.NamespaceName ), new XElement( XName.Get( "tx", DocX.c.NamespaceName ), new XElement( XName.Get( "strRef", DocX.c.NamespaceName ), new XElement( XName.Get( "f", DocX.c.NamespaceName ), "" ), new XElement( XName.Get( "strCache", DocX.c.NamespaceName ), new XElement( XName.Get( "pt", DocX.c.NamespaceName ), new XAttribute( XName.Get( "idx" ), "0" ), new XElement( XName.Get( "v", DocX.c.NamespaceName ), name ) ) ) ) ), new XElement( XName.Get( "invertIfNegative", DocX.c.NamespaceName ), "0" ), new XElement( XName.Get( "cat", DocX.c.NamespaceName ), new XElement( XName.Get( "strRef", DocX.c.NamespaceName ), new XElement( XName.Get( "f", DocX.c.NamespaceName ), "" ), _strCache ) ), new XElement( XName.Get( "val", DocX.c.NamespaceName ), new XElement( XName.Get( "numRef", DocX.c.NamespaceName ), new XElement( XName.Get( "f", DocX.c.NamespaceName ), "" ), _numCache ) ) ); } #endregion #region Public Methods public void Bind( ICollection list, String categoryPropertyName, String valuePropertyName ) { var ptCount = new XElement( XName.Get( "ptCount", DocX.c.NamespaceName ), new XAttribute( XName.Get( "val" ), list.Count ) ); var formatCode = new XElement( XName.Get( "formatCode", DocX.c.NamespaceName ), "General" ); _strCache.RemoveAll(); _numCache.RemoveAll(); _strCache.Add( ptCount ); _numCache.Add( formatCode ); _numCache.Add( ptCount ); Int32 index = 0; XElement pt; foreach( var item in list ) { pt = new XElement( XName.Get( "pt", DocX.c.NamespaceName ), new XAttribute( XName.Get( "idx" ), index ), new XElement( XName.Get( "v", DocX.c.NamespaceName ), item.GetType().GetProperty( categoryPropertyName ).GetValue( item, null ) ) ); _strCache.Add( pt ); pt = new XElement( XName.Get( "pt", DocX.c.NamespaceName ), new XAttribute( XName.Get( "idx" ), index ), new XElement( XName.Get( "v", DocX.c.NamespaceName ), item.GetType().GetProperty( valuePropertyName ).GetValue( item, null ) ) ); _numCache.Add( pt ); index++; } } public void Bind( IList categories, IList values ) { if( categories.Count != values.Count ) throw new ArgumentException( "Categories count must equal to Values count" ); var ptCount = new XElement( XName.Get( "ptCount", DocX.c.NamespaceName ), new XAttribute( XName.Get( "val" ), categories.Count ) ); var formatCode = new XElement( XName.Get( "formatCode", DocX.c.NamespaceName ), "General" ); _strCache.RemoveAll(); _numCache.RemoveAll(); _strCache.Add( ptCount ); _numCache.Add( formatCode ); _numCache.Add( ptCount ); XElement pt; for( int index = 0; index < categories.Count; index++ ) { pt = new XElement( XName.Get( "pt", DocX.c.NamespaceName ), new XAttribute( XName.Get( "idx" ), index ), new XElement( XName.Get( "v", DocX.c.NamespaceName ), categories[ index ].ToString() ) ); _strCache.Add( pt ); pt = new XElement( XName.Get( "pt", DocX.c.NamespaceName ), new XAttribute( XName.Get( "idx" ), index ), new XElement( XName.Get( "v", DocX.c.NamespaceName ), values[ index ].ToString() ) ); _numCache.Add( pt ); } } #endregion } /// /// Represents a chart legend /// More: http://msdn.microsoft.com/ru-ru/library/cc845123.aspx /// public class ChartLegend { #region Public Properties /// /// Specifies that other chart elements shall be allowed to overlap this chart element /// public Boolean Overlay { get { return Xml.Element( XName.Get( "overlay", DocX.c.NamespaceName ) ).Attribute( "val" ).Value == "1"; } set { Xml.Element( XName.Get( "overlay", DocX.c.NamespaceName ) ).Attribute( "val" ).Value = GetOverlayValue( value ); } } /// /// Specifies the possible positions for a legend /// public ChartLegendPosition Position { get { return XElementHelpers.GetValueToEnum( Xml.Element( XName.Get( "legendPos", DocX.c.NamespaceName ) ) ); } set { XElementHelpers.SetValueFromEnum( Xml.Element( XName.Get( "legendPos", DocX.c.NamespaceName ) ), value ); } } #endregion #region Internal Properties /// /// Legend xml element /// internal XElement Xml { get; private set; } #endregion #region Constructors internal ChartLegend( ChartLegendPosition position, Boolean overlay ) { Xml = new XElement( XName.Get( "legend", DocX.c.NamespaceName ), new XElement( XName.Get( "legendPos", DocX.c.NamespaceName ), new XAttribute( "val", XElementHelpers.GetXmlNameFromEnum( position ) ) ), new XElement( XName.Get( "overlay", DocX.c.NamespaceName ), new XAttribute( "val", GetOverlayValue( overlay ) ) ) ); } #endregion #region Private Methods /// /// ECMA-376, page 3840 /// 21.2.2.132 overlay (Overlay) /// private String GetOverlayValue( Boolean overlay ) { if( overlay ) return "1"; else return "0"; } #endregion } /// /// Specifies the possible positions for a legend. /// 21.2.3.24 ST_LegendPos (Legend Position) /// public enum ChartLegendPosition { [XmlName( "t" )] Top, [XmlName( "b" )] Bottom, [XmlName( "l" )] Left, [XmlName( "r" )] Right, [XmlName( "tr" )] TopRight } /// /// Specifies the possible ways to display blanks. /// 21.2.3.10 ST_DispBlanksAs (Display Blanks As) /// public enum DisplayBlanksAs { [XmlName( "gap" )] Gap, [XmlName( "span" )] Span, [XmlName( "zero" )] Zero } }