Manage Presentation Comments in .NET
Overview
This article explains how to manage presentation comments with Aspose.Slides for .NET. It introduces the main comment-related types and demonstrates how to add comments to slides, access existing comments, work with replies and modern comments, and remove comments from a presentation.
The examples cover common review and collaboration scenarios in PowerPoint, such as assigning comments to authors, reading comment text and metadata, building reply chains, and removing selected comments or all comments.
In PowerPoint, comments appear as annotations on slides. Selecting a comment displays its text and related discussion.
Why Add Comments to Presentations?
You can use comments to provide feedback and collaborate with colleagues when reviewing presentations.
Aspose.Slides for .NET provides the following APIs for working with comments:
- The Presentation class, which provides access to the presentation’s comment authors.
- The ICommentCollection interface, which represents the comments associated with an individual author.
- The IComment interface, which provides information about a comment, including its author, creation time, position, and text.
- The CommentAuthor class, which provides information about an author, including their name, initials, and associated comments.
Add Slide Comments
The following example shows how to add comments to slides in a PowerPoint presentation:
using System;
using System.Drawing;
using Aspose.Slides;
using Aspose.Slides.Export;
using var presentation = new Presentation();
var firstSlide = presentation.Slides[0];
var secondSlide = presentation.Slides.AddEmptySlide(presentation.LayoutSlides[0]);
var author = presentation.CommentAuthors.AddAuthor("Jawad", "MF");
var position = new PointF(0.2f, 0.2f);
var createdTime = DateTime.Now;
author.Comments.AddComment("Hello Jawad, this is a slide comment", firstSlide, position, createdTime);
author.Comments.AddComment("Hello Jawad, this is the second slide comment", secondSlide, position, createdTime);
var comments = firstSlide.GetSlideComments(author);
if (comments.Length > 0)
{
var firstComment = comments[0];
Console.WriteLine(firstComment.Text);
var commentText = firstComment.Author.Comments[0].Text;
Console.WriteLine(commentText);
}
presentation.Save("Comments_out.pptx", SaveFormat.Pptx);
Access Slide Comments
The following example shows how to access existing comments in a PowerPoint presentation:
using System;
using Aspose.Slides;
using var presentation = new Presentation("Comments1.pptx");
foreach (var author in presentation.CommentAuthors)
{
foreach (var comment in author.Comments)
{
Console.WriteLine($"Slide: {comment.Slide.SlideNumber}");
Console.WriteLine($"Comment: {comment.Text}");
Console.WriteLine($"Author: {comment.Author.Name}");
Console.WriteLine($"Posted at: {comment.CreatedTime}");
Console.WriteLine();
}
}
Reply to Comments
A parent comment is the original comment at the top of a reply hierarchy. The ParentComment property of the IComment interface lets you get or set the parent of a comment.
The following example shows how to add replies and inspect the resulting comment hierarchy:
using System;
using System.Drawing;
using Aspose.Slides;
using Aspose.Slides.Export;
using var presentation = new Presentation();
var slide = presentation.Slides[0];
var position = new PointF(10, 10);
var createdTime = DateTime.Now;
var author1 = presentation.CommentAuthors.AddAuthor("Author_1", "A.A.");
var comment1 = author1.Comments.AddComment("comment 1", slide, position, createdTime);
var author2 = presentation.CommentAuthors.AddAuthor("Author_2", "B.B.");
var reply1 = author2.Comments.AddComment("reply 1 for comment 1", slide, position, createdTime);
reply1.ParentComment = comment1;
var reply2 = author2.Comments.AddComment("reply 2 for comment 1", slide, position, createdTime);
reply2.ParentComment = comment1;
var subReply = author1.Comments.AddComment("subreply 3 for reply 2", slide, position, createdTime);
subReply.ParentComment = reply2;
author2.Comments.AddComment("comment 2", slide, position, createdTime);
var comment3 = author2.Comments.AddComment("comment 3", slide, position, createdTime);
var reply3 = author1.Comments.AddComment("reply 4 for comment 3", slide, position, createdTime);
reply3.ParentComment = comment3;
var comments = slide.GetSlideComments(null);
for (var i = 0; i < comments.Length; i++)
{
var comment = comments[i];
while (comment.ParentComment != null)
{
Console.Write("\t");
comment = comment.ParentComment;
}
Console.WriteLine($"{comments[i].Author.Name}: {comments[i].Text}");
}
presentation.Save("parent_comment.pptx", SaveFormat.Pptx);
comment1.Remove();
presentation.Save("remove_comment.pptx", SaveFormat.Pptx);
Attention
- When the Remove method of the IComment interface is used to delete a comment, all replies to that comment are also deleted.
- If the ParentComment property creates a circular reference, a PptxEditException is thrown.
Add Modern Comments
Modern comments can be associated with the slide itself, with a specific shape, or with a text range inside an AutoShape. The ICommentCollection.AddModernComment method accepts an IShape argument in addition to the slide and comment-marker coordinates.
When null is passed for the shape argument, the comment is a slide-level comment. Its marker is positioned by the supplied coordinates, but it is not associated with a particular shape, so IModernComment.Shape returns null. When an IShape is supplied, the comment is anchored to that shape. The coordinates still define the position of the comment marker on the slide, while the shape association can be retrieved through IModernComment.Shape.
Anchor a Modern Comment to a Shape
The following example creates both a slide-level modern comment and a modern comment anchored to a specific AutoShape. It then reads the associated shape from each comment.
using System;
using System.Drawing;
using Aspose.Slides;
using Aspose.Slides.Export;
using var presentation = new Presentation();
var slide = presentation.Slides[0];
var author = presentation.CommentAuthors.AddAuthor("Reviewer", "RV");
var shape = slide.Shapes.AddAutoShape(ShapeType.Rectangle, 50, 50, 300, 80);
shape.Name = "Revenue title";
shape.TextFrame.Text = "Quarterly revenue";
var createdTime = DateTime.Now;
var slideCommentPosition = new PointF(20, 20);
var shapeCommentPosition = new PointF(60, 60);
var slideComment = author.Comments.AddModernComment("Review the overall slide layout.", slide, null, slideCommentPosition, createdTime);
var shapeComment = author.Comments.AddModernComment("Check this title.", slide, shape, shapeCommentPosition, createdTime);
Console.WriteLine(slideComment.Shape == null);
Console.WriteLine(shapeComment.Shape?.Name);
presentation.Save("modern_comments.pptx", SaveFormat.Pptx);
Anchor Comments to Different Shape Types
Any slide object that implements IShape can be used as a shape anchor. Common examples include IAutoShape, IPictureFrame, IGroupShape, IConnector, and IGraphicalObject instances such as charts.
The following example creates several common shape types and associates a modern comment with each one.
using System;
using System.Drawing;
using Aspose.Slides;
using Aspose.Slides.Charts;
using Aspose.Slides.Export;
using var presentation = new Presentation();
var slide = presentation.Slides[0];
var author = presentation.CommentAuthors.AddAuthor("Reviewer", "RV");
var createdTime = DateTime.Now;
var autoShape = slide.Shapes.AddAutoShape(ShapeType.Rectangle, 20, 20, 180, 60);
autoShape.TextFrame.Text = "AutoShape";
var autoShapeCommentPosition = new PointF(30, 30);
author.Comments.AddModernComment("Comment on an AutoShape.", slide, autoShape, autoShapeCommentPosition, createdTime);
var imageBase64 = "iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91JpzAAAAFklEQVR4nGP8//8/AwMDEwMDAwMDAwAkBgMB/DXemwAAAABJRU5ErkJggg==";
var imageData = Convert.FromBase64String(imageBase64);
var image = presentation.Images.AddImage(imageData);
var pictureFrame = slide.Shapes.AddPictureFrame(ShapeType.Rectangle, 220, 20, 120, 80, image);
var pictureCommentPosition = new PointF(230, 30);
author.Comments.AddModernComment("Comment on a picture.", slide, pictureFrame, pictureCommentPosition, createdTime);
var groupShape = slide.Shapes.AddGroupShape();
groupShape.Shapes.AddAutoShape(ShapeType.Rectangle, 0, 0, 80, 40);
groupShape.Shapes.AddAutoShape(ShapeType.Ellipse, 100, 0, 80, 40);
var groupCommentPosition = new PointF(40, 150);
author.Comments.AddModernComment("Comment on a group.", slide, groupShape, groupCommentPosition, createdTime);
var connector = slide.Shapes.AddConnector(ShapeType.StraightConnector1, 220, 150, 140, 40);
var connectorCommentPosition = new PointF(240, 150);
author.Comments.AddModernComment("Comment on a connector.", slide, connector, connectorCommentPosition, createdTime);
var chart = slide.Shapes.AddChart(ChartType.ClusteredColumn, 400, 20, 250, 180);
var chartCommentPosition = new PointF(420, 40);
author.Comments.AddModernComment("Comment on a graphical object.", slide, chart, chartCommentPosition, createdTime);
presentation.Save("modern_comment_shape_types.pptx", SaveFormat.Pptx);
Anchor a Comment to Text and Set Its Status
For a modern comment associated with an IAutoShape, IModernComment.TextSelectionStart specifies the starting position of the selected text in the shape’s text frame, while IModernComment.TextSelectionLength specifies the length of the selection. Together, these properties associate the comment with a specific text range inside the AutoShape.
The IModernComment.Status property can be read or updated with a value from the ModernCommentStatus enumeration:
NotDefined— no specific modern-comment status is defined.Active— the comment is active.Resolved— the comment has been resolved.Closed— the comment is closed.
The following example creates a shape-anchored modern comment, associates it with a text selection, marks it as resolved, saves the presentation, and verifies the values after reopening the file.
using System;
using System.Drawing;
using Aspose.Slides;
using Aspose.Slides.Export;
const string outputFile = "modern_comment_text_anchor.pptx";
const string shapeText = "Review the quarterly revenue forecast.";
const string selectedText = "quarterly revenue";
var expectedSelectionStart = shapeText.IndexOf(selectedText, StringComparison.Ordinal);
using var presentation = new Presentation();
var slide = presentation.Slides[0];
var shape = slide.Shapes.AddAutoShape(ShapeType.Rectangle, 50, 50, 400, 100);
shape.Name = "Forecast text";
shape.TextFrame.Text = shapeText;
var author = presentation.CommentAuthors.AddAuthor("Reviewer", "RV");
var commentPosition = new PointF(60, 60);
var comment = author.Comments.AddModernComment("Verify this forecast wording.", slide, shape, commentPosition, DateTime.Now);
comment.TextSelectionStart = expectedSelectionStart;
comment.TextSelectionLength = selectedText.Length;
comment.Status = ModernCommentStatus.Resolved;
presentation.Save(outputFile, SaveFormat.Pptx);
using var reopenedPresentation = new Presentation(outputFile);
var reopenedSlide = reopenedPresentation.Slides[0];
var reopenedComments = reopenedSlide.GetSlideComments(null);
foreach (var reopenedComment in reopenedComments)
{
if (reopenedComment is not IModernComment modernComment)
{
continue;
}
var shapeMatches = modernComment.Shape?.Name == "Forecast text";
var selectionStartMatches = modernComment.TextSelectionStart == expectedSelectionStart;
var selectionLengthMatches = modernComment.TextSelectionLength == selectedText.Length;
var statusMatches = modernComment.Status == ModernCommentStatus.Resolved;
Console.WriteLine($"Shape anchor preserved: {shapeMatches}");
Console.WriteLine($"Text selection start preserved: {selectionStartMatches}");
Console.WriteLine($"Text selection length preserved: {selectionLengthMatches}");
Console.WriteLine($"Resolved status preserved: {statusMatches}");
}
Inspect Existing Modern Comments
To inspect an existing presentation, check which comments implement IModernComment, then examine IModernComment.Shape, IModernComment.TextSelectionStart, IModernComment.TextSelectionLength, and IModernComment.Status. A null shape indicates a slide-level comment. For an IAutoShape anchor, the text-selection properties identify the associated range in the shape’s text frame.
using System;
using Aspose.Slides;
using var presentation = new Presentation("comments.pptx");
foreach (var slide in presentation.Slides)
{
var comments = slide.GetSlideComments(null);
foreach (var comment in comments)
{
if (comment is not IModernComment modernComment)
{
continue;
}
Console.WriteLine($"Slide: {slide.SlideNumber}");
Console.WriteLine($"Text: {modernComment.Text}");
Console.WriteLine($"Status: {modernComment.Status}");
var shape = modernComment.Shape;
if (shape == null)
{
Console.WriteLine("Anchor: slide level");
}
else
{
Console.WriteLine($"Anchor shape: {shape.Name}");
Console.WriteLine($"Anchor type: {shape.GetType().Name}");
if (shape is IAutoShape)
{
Console.WriteLine($"Text selection start: {modernComment.TextSelectionStart}");
Console.WriteLine($"Text selection length: {modernComment.TextSelectionLength}");
}
}
Console.WriteLine();
}
}
Remove Comments
Remove All Comments and Comment Authors
The following example shows how to remove all comments and comment authors from a presentation:
using Aspose.Slides;
using Aspose.Slides.Export;
using var presentation = new Presentation("example.pptx");
foreach (var author in presentation.CommentAuthors)
{
author.Comments.Clear();
}
presentation.CommentAuthors.Clear();
presentation.Save("example_out.pptx", SaveFormat.Pptx);
Remove Specific Comments
The following example shows how to remove specific comments from a slide:
using System;
using System.Collections.Generic;
using System.Drawing;
using Aspose.Slides;
using Aspose.Slides.Export;
using var presentation = new Presentation();
var slide = presentation.Slides[0];
var author = presentation.CommentAuthors.AddAuthor("Author", "A");
var createdTime = DateTime.Now;
var firstCommentPosition = new PointF(0.2f, 0.2f);
var secondCommentPosition = new PointF(0.3f, 0.2f);
author.Comments.AddComment("comment 1", slide, firstCommentPosition, createdTime);
author.Comments.AddComment("comment 2", slide, secondCommentPosition, createdTime);
foreach (var commentAuthor in presentation.CommentAuthors)
{
var commentsToRemove = new List<IComment>();
var comments = slide.GetSlideComments(commentAuthor);
foreach (var comment in comments)
{
if (comment.Text == "comment 1")
{
commentsToRemove.Add(comment);
}
}
foreach (var comment in commentsToRemove)
{
commentAuthor.Comments.Remove(comment);
}
}
presentation.Save("pres.pptx", SaveFormat.Pptx);
FAQ
Does Aspose.Slides support a resolved status for modern comments?
Yes. IModernComment.Status can be read and set with a ModernCommentStatus value, including Resolved. The status is stored in the presentation and can be read again after the file is reopened.
Are threaded discussions (reply chains) supported, and is there a nesting limit?
Yes. Each comment can reference its parent comment, enabling reply chains. The API does not define a specific nesting-depth limit.
In what coordinate system is a comment marker’s position defined on a slide?
The marker position is defined by floating-point coordinates in the slide coordinate system, allowing you to place it precisely on the slide.