Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

List.cs 9.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  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.Linq;
  11. using System.Xml.Linq;
  12. using System.Collections.Generic;
  13. namespace Xceed.Words.NET
  14. {
  15. /// <summary>
  16. /// Represents a List in a document.
  17. /// </summary>
  18. public class List : InsertBeforeOrAfter
  19. {
  20. #region Public Members
  21. /// <summary>
  22. /// This is a list of paragraphs that will be added to the document
  23. /// when the list is inserted into the document.
  24. /// The paragraph needs a numPr defined to be in this items collection.
  25. /// </summary>
  26. public List<Paragraph> Items
  27. {
  28. get; private set;
  29. }
  30. /// <summary>
  31. /// The numId used to reference the list settings in the numbering.xml
  32. /// </summary>
  33. public int NumId
  34. {
  35. get; private set;
  36. }
  37. /// <summary>
  38. /// The ListItemType (bullet or numbered) of the list.
  39. /// </summary>
  40. public ListItemType? ListType
  41. {
  42. get; private set;
  43. }
  44. #endregion
  45. #region Constructors
  46. internal List( DocX document, XElement xml )
  47. : base( document, xml )
  48. {
  49. Items = new List<Paragraph>();
  50. ListType = null;
  51. }
  52. #endregion
  53. #region Public Methods
  54. /// <summary>
  55. /// Adds an item to the list.
  56. /// </summary>
  57. /// <param name="paragraph"></param>
  58. /// <exception cref="InvalidOperationException">
  59. /// Throws an InvalidOperationException if the item cannot be added to the list.
  60. /// </exception>
  61. public void AddItem( Paragraph paragraph )
  62. {
  63. if( paragraph.IsListItem )
  64. {
  65. var numIdNode = paragraph.Xml.Descendants().FirstOrDefault( s => s.Name.LocalName == "numId" );
  66. if( numIdNode == null )
  67. return;
  68. var numId = Int32.Parse( numIdNode.Attribute( DocX.w + "val" ).Value );
  69. if( CanAddListItem( paragraph ) )
  70. {
  71. NumId = numId;
  72. Items.Add( paragraph );
  73. }
  74. else
  75. throw new InvalidOperationException( "New list items can only be added to this list if they are have the same numId." );
  76. }
  77. }
  78. public void AddItemWithStartValue( Paragraph paragraph, int start )
  79. {
  80. //TODO: Update the numbering
  81. UpdateNumberingForLevelStartNumber( int.Parse( paragraph.IndentLevel.ToString() ), start );
  82. if( ContainsLevel( start ) )
  83. throw new InvalidOperationException( "Cannot add a paragraph with a start value if another element already exists in this list with that level." );
  84. AddItem( paragraph );
  85. }
  86. /// <summary>
  87. /// Determine if it is able to add the item to the list
  88. /// </summary>
  89. /// <param name="paragraph"></param>
  90. /// <returns>
  91. /// Return true if AddItem(...) will succeed with the given paragraph.
  92. /// </returns>
  93. public bool CanAddListItem( Paragraph paragraph )
  94. {
  95. if( paragraph.IsListItem )
  96. {
  97. //var lvlNode = paragraph.Xml.Descendants().First(s => s.Name.LocalName == "ilvl");
  98. var numIdNode = paragraph.Xml.Descendants().FirstOrDefault( s => s.Name.LocalName == "numId" );
  99. if( numIdNode == null )
  100. return false;
  101. var numId = Int32.Parse( numIdNode.Attribute( DocX.w + "val" ).Value );
  102. //Level = Int32.Parse(lvlNode.Attribute(DocX.w + "val").Value);
  103. if( NumId == 0 || ( numId == NumId && numId > 0 ) )
  104. {
  105. return true;
  106. }
  107. }
  108. return false;
  109. }
  110. public bool ContainsLevel( int ilvl )
  111. {
  112. return Items.Any( i => i.ParagraphNumberProperties.Descendants().First( el => el.Name.LocalName == "ilvl" ).Value == ilvl.ToString() );
  113. }
  114. #endregion
  115. #region Internal Methods
  116. internal void CreateNewNumberingNumId( int level = 0, ListItemType listType = ListItemType.Numbered, int? startNumber = null, bool continueNumbering = false )
  117. {
  118. ValidateDocXNumberingPartExists();
  119. if( Document._numbering.Root == null )
  120. {
  121. throw new InvalidOperationException( "Numbering section did not instantiate properly." );
  122. }
  123. ListType = listType;
  124. var numId = GetMaxNumId() + 1;
  125. var abstractNumId = GetMaxAbstractNumId() + 1;
  126. XDocument listTemplate;
  127. switch( listType )
  128. {
  129. case ListItemType.Bulleted:
  130. listTemplate = HelperFunctions.DecompressXMLResource( "Xceed.Words.NET.Resources.numbering.default_bullet_abstract.xml.gz" );
  131. break;
  132. case ListItemType.Numbered:
  133. listTemplate = HelperFunctions.DecompressXMLResource( "Xceed.Words.NET.Resources.numbering.default_decimal_abstract.xml.gz" );
  134. break;
  135. default:
  136. throw new InvalidOperationException( string.Format( "Unable to deal with ListItemType: {0}.", listType.ToString() ) );
  137. }
  138. var abstractNumTemplate = listTemplate.Descendants().Single( d => d.Name.LocalName == "abstractNum" );
  139. abstractNumTemplate.SetAttributeValue( DocX.w + "abstractNumId", abstractNumId );
  140. var abstractNumXml = GetAbstractNumXml( abstractNumId, numId, startNumber, continueNumbering );
  141. var abstractNumNode = Document._numbering.Root.Descendants().LastOrDefault( xElement => xElement.Name.LocalName == "abstractNum" );
  142. var numXml = Document._numbering.Root.Descendants().LastOrDefault( xElement => xElement.Name.LocalName == "num" );
  143. if( abstractNumNode == null || numXml == null )
  144. {
  145. Document._numbering.Root.Add( abstractNumTemplate );
  146. Document._numbering.Root.Add( abstractNumXml );
  147. }
  148. else
  149. {
  150. abstractNumNode.AddAfterSelf( abstractNumTemplate );
  151. numXml.AddAfterSelf(
  152. abstractNumXml
  153. );
  154. }
  155. NumId = numId;
  156. }
  157. /// <summary>
  158. /// Get the abstractNum definition for the given numId
  159. /// </summary>
  160. /// <param name="numId">The numId on the pPr element</param>
  161. /// <returns>XElement representing the requested abstractNum</returns>
  162. internal XElement GetAbstractNum( int numId )
  163. {
  164. return HelperFunctions.GetAbstractNum( this.Document, numId.ToString() );
  165. }
  166. #endregion
  167. #region Private Methods
  168. private void UpdateNumberingForLevelStartNumber( int iLevel, int start )
  169. {
  170. var abstractNum = GetAbstractNum( NumId );
  171. var level = abstractNum.Descendants().First( el => el.Name.LocalName == "lvl" && el.GetAttribute( DocX.w + "ilvl" ) == iLevel.ToString() );
  172. level.Descendants().First( el => el.Name.LocalName == "start" ).SetAttributeValue( DocX.w + "val", start );
  173. }
  174. private XElement GetAbstractNumXml( int abstractNumId, int numId, int? startNumber, bool continueNumbering )
  175. {
  176. var start = new XElement( XName.Get( "startOverride", DocX.w.NamespaceName ), new XAttribute( DocX.w + "val", startNumber ?? 1 ) );
  177. var level = new XElement( XName.Get( "lvlOverride", DocX.w.NamespaceName ), new XAttribute( DocX.w + "ilvl", 0 ), start );
  178. var element = new XElement( XName.Get( "abstractNumId", DocX.w.NamespaceName ), new XAttribute( DocX.w + "val", abstractNumId ) );
  179. return continueNumbering
  180. ? new XElement( XName.Get( "num", DocX.w.NamespaceName ), new XAttribute( DocX.w + "numId", numId ), element )
  181. : new XElement( XName.Get( "num", DocX.w.NamespaceName ), new XAttribute( DocX.w + "numId", numId ), element, level );
  182. }
  183. /// <summary>
  184. /// Method to determine the last numId for a list element.
  185. /// Also useful for determining the next numId to use for inserting a new list element into the document.
  186. /// </summary>
  187. /// <returns>
  188. /// 0 if there are no elements in the list already.
  189. /// Increment the return for the next valid value of a new list element.
  190. /// </returns>
  191. private int GetMaxNumId()
  192. {
  193. const int defaultValue = 0;
  194. if( Document._numbering == null )
  195. return defaultValue;
  196. var numlist = Document._numbering.Descendants().Where( d => d.Name.LocalName == "num" ).ToList();
  197. if( numlist.Any() )
  198. return numlist.Attributes( DocX.w + "numId" ).Max( e => int.Parse( e.Value ) );
  199. return defaultValue;
  200. }
  201. /// <summary>
  202. /// Method to determine the last abstractNumId for a list element.
  203. /// Also useful for determining the next abstractNumId to use for inserting a new list element into the document.
  204. /// </summary>
  205. /// <returns>
  206. /// -1 if there are no elements in the list already.
  207. /// Increment the return for the next valid value of a new list element.
  208. /// </returns>
  209. private int GetMaxAbstractNumId()
  210. {
  211. const int defaultValue = -1;
  212. if( Document._numbering == null )
  213. return defaultValue;
  214. var numlist = Document._numbering.Descendants().Where( d => d.Name.LocalName == "abstractNum" ).ToList();
  215. if( numlist.Any() )
  216. {
  217. var maxAbstractNumId = numlist.Attributes( DocX.w + "abstractNumId" ).Max( e => int.Parse( e.Value ) );
  218. return maxAbstractNumId;
  219. }
  220. return defaultValue;
  221. }
  222. private void ValidateDocXNumberingPartExists()
  223. {
  224. var numberingUri = new Uri( "/word/numbering.xml", UriKind.Relative );
  225. // If the internal document contains no /word/numbering.xml create one.
  226. if( !Document._package.PartExists( numberingUri ) )
  227. Document._numbering = HelperFunctions.AddDefaultNumberingXml( Document._package );
  228. }
  229. #endregion
  230. }
  231. }