none
Removing multiple parts at one time... but only one is saved (PresentationML) RRS feed

  • Question

  • Working on a script to remove all placeholder shapes (except the slide & notes placeholders) in a notes slide and replace them with new ones. My process goes like this:

    1. Open presentation
    2. Loop through all slides
    3. For each slide's NotesSlide: remove all shapes of type Header/Footer/DateAndTime/SlideNumber
    4. For each slide's NotesSlide: add new Header/Footer/DateAndTime/SlideNumber
    5. Close the presentation

    However, in step 3, the method that does this, while it finds all the parts, it doesn't remove them. I have to call this method multiple times to remove the items. I can see that my LINQ query gets all the shapes I want to remove, but only one is actually removed each time the method runs. Here's the method:

    private static void PurgeCustomSlideNotesLayout(SlidePart slidePart)
    {
      NotesSlidePart notesSlidePart = slidePart.NotesSlidePart;
      if (notesSlidePart == null)
        return;
    
      var shapes = from shape in notesSlidePart.NotesSlide.CommonSlideData.ShapeTree.Descendants<Shape>()
                   where IsShapeType(shape, PlaceholderValues.Header) ||
                      IsShapeType(shape, PlaceholderValues.DateAndTime) ||
                      IsShapeType(shape, PlaceholderValues.Footer) ||
                      IsShapeType(shape, PlaceholderValues.SlideNumber)
                   select shape;
    
      //Console.WriteLine("Slide: " + slidePart.Uri.ToString());
      //Console.WriteLine("Parts to remove:" + shapes.Count().ToString());
    
      foreach (var shape in shapes)
      {
        notesSlidePart.NotesSlide.CommonSlideData.ShapeTree.RemoveChild(shape);
      }
    
    }
    
    private static bool IsShapeType(Shape shape, PlaceholderValues shapeType)
    {
      var placeholderShape = shape.NonVisualShapeProperties.ApplicationNonVisualDrawingProperties.GetFirstChild<PlaceholderShape>();
      if (placeholderShape.Type == shapeType)
        return true;
      else return false;
    }
    

    Can't figure this one out... looping through them to remove the shapes, but it's not working the way I'd expect. Ideas?

    Using OpenXML SDK 2.0...


    -AC [MVP SharePoint Server] http://www.andrewconnell.com/blog
    Tuesday, March 30, 2010 2:07 PM

Answers

  • Hello Andrew,

    I write some codes to test in my side. I think I am able to reproduce what you describe. In the codes of foreach section,

      foreach (var shape in shapes)
      {
        notesSlidePart.NotesSlide.CommonSlideData.ShapeTree.RemoveChild(shape);
      }

    When debugging, we can see the shapes.Count().ToString() returns, for example, 3. But once the RemoveChild is executed, the codes quit the foreach iteration.

    I think we cannot remove an item from the returned IEnumberable when iterating. That would modify the enumberable collection and break the iteration. So if we want to remove an item, we first call ToList() to convert the IEnumberable to IList collection. Then it would work. I try the following codes which work fine.

    ---------------------------------------------------------

            static void Main(string[] args)
            {
                PresentationDocument presentation = PresentationDocument.Open(@"D:\test.pptx", true);
               
                foreach (SlidePart sp in presentation.PresentationPart.SlideParts)
                {
                    PurgeCustomSlideNotesLayout(sp);
                }
               
                presentation.Close();
            }

            private static void PurgeCustomSlideNotesLayout(SlidePart slidePart)
            {
                NotesSlidePart notesSlidePart = slidePart.NotesSlidePart;
                if (notesSlidePart == null)
                    return;

                var shapes = from shape in notesSlidePart.NotesSlide.CommonSlideData.ShapeTree.Descendants<Shape>()
                             where IsShapeType(shape, PlaceholderValues.Header) ||
                                IsShapeType(shape, PlaceholderValues.DateAndTime) ||
                                IsShapeType(shape, PlaceholderValues.Footer) ||
                                IsShapeType(shape, PlaceholderValues.SlideNumber)
                             select shape;

                Debug.Print("Slide: " + slidePart.Uri.ToString());
                Debug.Print("Parts to remove:" + shapes.Count().ToString());

                IList<Shape> shs = shapes.ToList();
                foreach (Shape shape in shs)
                {
                    notesSlidePart.NotesSlide.CommonSlideData.ShapeTree.RemoveChild(shape);
                }

            }

            private static bool IsShapeType(Shape shape, PlaceholderValues shapeType)
            {
                var placeholderShape = shape.NonVisualShapeProperties.ApplicationNonVisualDrawingProperties.GetFirstChild<PlaceholderShape>();
                if (placeholderShape.Type == shapeType)
                    return true;
                else return false;
            }

    ---------------------------------------------------------

    Could you please give it a try and let me know if it works for you?

     

    Regards,
    Ji Zhou
    MSDN Subscriber Support in Forum
    If you have any feedback of our support, please contact msdnmg@microsoft.com.


    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Thursday, April 1, 2010 6:30 AM
    Moderator

All replies

  • Hi Andrew Connell,

    Thanks for your question.

    Have you tried out the Productivity Tool to solve your problem? You could follow these steps:

    1. Make some changes as you need to the source file and save it as a target file.

    2. Use "Compare Files" to open both the source and target file, then you will see the differences between them.

    3. Click "View Package Code" to see the sample code generated by the Tool on how to change from the source file to the targe file.

    Hope this helps. If you have any question, please let me know.

    Thanks,

    Lu

    Thursday, April 1, 2010 5:34 AM
  • Hi Andrew Connell,

    Thanks for your question.

    Have you tried out the Productivity Tool to solve your problem? You could follow these steps:

    1. Make some changes as you need to the source file and save it as a target file.

    2. Use "Compare Files" to open both the source and target file, then you will see the differences between them.

    3. Click "View Package Code" to see the sample code generated by the Tool on how to change from the source file to the targe file.

    Hope this helps. If you have any question, please let me know.

    Thanks,

    Lu

    Thursday, April 1, 2010 5:34 AM
  • Hello Andrew,

    I write some codes to test in my side. I think I am able to reproduce what you describe. In the codes of foreach section,

      foreach (var shape in shapes)
      {
        notesSlidePart.NotesSlide.CommonSlideData.ShapeTree.RemoveChild(shape);
      }

    When debugging, we can see the shapes.Count().ToString() returns, for example, 3. But once the RemoveChild is executed, the codes quit the foreach iteration.

    I think we cannot remove an item from the returned IEnumberable when iterating. That would modify the enumberable collection and break the iteration. So if we want to remove an item, we first call ToList() to convert the IEnumberable to IList collection. Then it would work. I try the following codes which work fine.

    ---------------------------------------------------------

            static void Main(string[] args)
            {
                PresentationDocument presentation = PresentationDocument.Open(@"D:\test.pptx", true);
               
                foreach (SlidePart sp in presentation.PresentationPart.SlideParts)
                {
                    PurgeCustomSlideNotesLayout(sp);
                }
               
                presentation.Close();
            }

            private static void PurgeCustomSlideNotesLayout(SlidePart slidePart)
            {
                NotesSlidePart notesSlidePart = slidePart.NotesSlidePart;
                if (notesSlidePart == null)
                    return;

                var shapes = from shape in notesSlidePart.NotesSlide.CommonSlideData.ShapeTree.Descendants<Shape>()
                             where IsShapeType(shape, PlaceholderValues.Header) ||
                                IsShapeType(shape, PlaceholderValues.DateAndTime) ||
                                IsShapeType(shape, PlaceholderValues.Footer) ||
                                IsShapeType(shape, PlaceholderValues.SlideNumber)
                             select shape;

                Debug.Print("Slide: " + slidePart.Uri.ToString());
                Debug.Print("Parts to remove:" + shapes.Count().ToString());

                IList<Shape> shs = shapes.ToList();
                foreach (Shape shape in shs)
                {
                    notesSlidePart.NotesSlide.CommonSlideData.ShapeTree.RemoveChild(shape);
                }

            }

            private static bool IsShapeType(Shape shape, PlaceholderValues shapeType)
            {
                var placeholderShape = shape.NonVisualShapeProperties.ApplicationNonVisualDrawingProperties.GetFirstChild<PlaceholderShape>();
                if (placeholderShape.Type == shapeType)
                    return true;
                else return false;
            }

    ---------------------------------------------------------

    Could you please give it a try and let me know if it works for you?

     

    Regards,
    Ji Zhou
    MSDN Subscriber Support in Forum
    If you have any feedback of our support, please contact msdnmg@microsoft.com.


    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Thursday, April 1, 2010 6:30 AM
    Moderator
  • Ji-

    Works for me... makes sense too... thanks Ji!


    -AC [MVP SharePoint Server] http://www.andrewconnell.com/blog
    Thursday, April 1, 2010 11:18 AM