Я делаю много презентаций, и часто нужно показать код «по частям», то есть показать ход мысли в процессе последовательного написния кода. К сожалению, PowerPoint не настроен для таких целей. Поэтому я решил написать расширение для PowerPoint 2007 которое автогенерировало бы последовательность слайдов с «прогрессивной разверткой» кода и комментариев. Собственно об этом и будет рассказ.
Сначала попробую объяснить идею. Есть у вас кусочек кода, скажем вот такой[1]:
Этот код хочется объяснить по частям на слайдах. Например, изначально можно показать вот это:
А потом, вот это:
И так далее. Казалось бы – можно использовать и встроенную анимацию[2], но это негибко – ведь даже если вы прячете, например, кусочек кода, то место для него все равно будет выделено, что не есть хорошо. А что если нужно спрятать не параграф или строчку, а только пару букв (например, ключевое слово)? Вот как раз для этих целей я решил написать свой второй[3] add-in для PowerPoint.
Дополнения для Office очень просто писать: для этого нужно воспользоваться дополнениями VSTO (Visual Studio Tools for Office). В студии появляются соответствующие типы проектов, из которых я выбрал PowerPoint 2007 Add-In:
Что очень радует, так это то что наш сгенерированный проект уже готов к использованию. В нем работает отладка по F5, и что приятно – даже если я не в студии, add-in остается в PowerPoint, что позволяет мне в свободное время «питаться своим же собачьим кормом».[4]
Поскольку в офисе используются ленточные контролы (ribbons), студия поддерживает работу именно с этими элементами управления. Для создания элементов ленточек есть даже специальный дизайнер:
Мне для интерфейса нужны былa всего одна кнопочка, что существенно упростило задачу (upd: теперь их уже 3):
У студии есть проблема (не знаю, может решили в 2010й), что в проект расширений, будь то расширений студии или офиса, нельзя добавить окна WPF. Обычно я решаю эту проблему с помощью отдельной сборки, но в данном случае захотелось попробовать пописать на WinForms, причем для интерфейса выбрал бесплатные (и достаточно красивые) контролы Krypton. Вот как это дело выглядит в завершенном варианте:
Неплохо, не правда ли? Все это бесплатно, и доступно тут. А вот то, как открывается окно из add-in’а – это стоит объяснить. Помните наш риббон? Так вот – в самом классе add-in’а он нигде не регистрируется! Риббон создается автоматически! А дальше все просто – к нашей кнопочке подвязано событие, которое собственно и делает предсказуемый вызов
Поскольку внутренняя логика add-in’а достаточно скучна, расскажу как программным путем создавать слайды в презентации. Прежде всего, нужно где-то держать ссылку на сам PowerPoint. Для этого, я просто создал статическую переменную и закэшировал в нее то значение, что получил при инициализации add-in’а:
Теперь наш обработчик кнопки (ну, тот что в риббоне) может получить эту же ссылку, а из нее – «активную» презентацию пользователя (т.е. ту, с которой пользователь работает):
Добавить новый слайд к презентации – проще простого. Помните типы layout’ов которые можно выбрать для слайда? Так вот, тут они—enum’ы, так что все очень просто:
Дальше – еще проще. У слайда с нашим раскладом
К слову замечу что использование ключевого слова
Вот пример того, как манипулировать свойствами текствой рамки. В моем случае я выставляю заданный пользователем шрифт а также убираю дефолтные поля и «буллиты»:
Заметьте что для шрифтов используется метод расширения. Проблема в том, что Office использует свой тип
Как вы уже догадались, текст кладется в объект типа
Теперь можно менять вкусовые качества этого отрезка текста – такие как шрифт или цвет:
Копирование шрифтов мы уже обсудили, а с цветом – та же проблема. Если использовать функцию
Тут пора бы уже и закончить, только вот хочу сделать одно предупреждение: если вы сделали параграф определенного цвета, то следующий параграф или кусочек кода будет такой же. Поэтому, когда строите текст из составных частей, сначала закэшируйте дефолтные значения, и применяйте их к каждому отрезку, который хотите добавить.
Работа с инфраструктурой VSTO оказалась весьма несложной, и результаты использования add-in’а скоро можно будет увидеть на моих презентациях в Spbalt.net, Ineta, ActiveMesa и других организациях. А если вас вдруг заинтересовал проект, то результат работы add-in’а а также исходный код можно скачать тут.
Идея
Сначала попробую объяснить идею. Есть у вас кусочек кода, скажем вот такой[1]:
public static T DeepCopy<T>(this IPrototype<T> obj)<br/>
{<br/>
T copy;<br/>
using (MemoryStream stream = new MemoryStream())<br/>
{<br/>
BinaryFormatter formatter = new BinaryFormatter();<br/>
formatter.Serialize(stream, obj);<br/>
stream.Seek(0, SeekOrigin.Begin);<br/>
copy = (T)formatter.Deserialize(stream);<br/>
stream.Close();<br/>
}<br/>
return copy;<br/>
}<br/>
Этот код хочется объяснить по частям на слайдах. Например, изначально можно показать вот это:
// создаем метод расширения
public static T DeepCopy<T>(this IPrototype<T> obj)<br/>
{<br/>
А потом, вот это:
// создаем метод расширения
public static T DeepCopy<T>(this IPrototype<T> obj)<br/>
{<br/>
// создаем копию объекта
T copy;<br/>
И так далее. Казалось бы – можно использовать и встроенную анимацию[2], но это негибко – ведь даже если вы прячете, например, кусочек кода, то место для него все равно будет выделено, что не есть хорошо. А что если нужно спрятать не параграф или строчку, а только пару букв (например, ключевое слово)? Вот как раз для этих целей я решил написать свой второй[3] add-in для PowerPoint.
Интерфейс
Дополнения для Office очень просто писать: для этого нужно воспользоваться дополнениями VSTO (Visual Studio Tools for Office). В студии появляются соответствующие типы проектов, из которых я выбрал PowerPoint 2007 Add-In:
Что очень радует, так это то что наш сгенерированный проект уже готов к использованию. В нем работает отладка по F5, и что приятно – даже если я не в студии, add-in остается в PowerPoint, что позволяет мне в свободное время «питаться своим же собачьим кормом».[4]
Поскольку в офисе используются ленточные контролы (ribbons), студия поддерживает работу именно с этими элементами управления. Для создания элементов ленточек есть даже специальный дизайнер:
Мне для интерфейса нужны былa всего одна кнопочка, что существенно упростило задачу (upd: теперь их уже 3):
Модальный интерфейс
У студии есть проблема (не знаю, может решили в 2010й), что в проект расширений, будь то расширений студии или офиса, нельзя добавить окна WPF. Обычно я решаю эту проблему с помощью отдельной сборки, но в данном случае захотелось попробовать пописать на WinForms, причем для интерфейса выбрал бесплатные (и достаточно красивые) контролы Krypton. Вот как это дело выглядит в завершенном варианте:
Неплохо, не правда ли? Все это бесплатно, и доступно тут. А вот то, как открывается окно из add-in’а – это стоит объяснить. Помните наш риббон? Так вот – в самом классе add-in’а он нигде не регистрируется! Риббон создается автоматически! А дальше все просто – к нашей кнопочке подвязано событие, которое собственно и делает предсказуемый вызов
ShowDialog()
.Работа со слайдами
Поскольку внутренняя логика add-in’а достаточно скучна, расскажу как программным путем создавать слайды в презентации. Прежде всего, нужно где-то держать ссылку на сам PowerPoint. Для этого, я просто создал статическую переменную и закэшировал в нее то значение, что получил при инициализации add-in’а:
public static Application App;<br/>
<br/>
private void OnStartup(object sender, EventArgs e)<br/>
{<br/>
App = Application;<br/>
if (ApplicationStartedEvent != null)<br/>
ApplicationStartedEvent(this, null);<br/>
}<br/>
Теперь наш обработчик кнопки (ну, тот что в риббоне) может получить эту же ссылку, а из нее – «активную» презентацию пользователя (т.е. ту, с которой пользователь работает):
var app = ThisAddIn.App;<br/>
var p = app.ActivePresentation;<br/>
Добавить новый слайд к презентации – проще простого. Помните типы layout’ов которые можно выбрать для слайда? Так вот, тут они—enum’ы, так что все очень просто:
Slide s = p.Slides.Add(<br/>
1+p.Slides.Count, // вставить последним
PpSlideLayout.ppLayoutText // одно большое текстовое поле
);<br/>
Дальше – еще проще. У слайда с нашим раскладом
ppLayoutText
есть заголовок и главное текстовое поле. Эти объекты доступны в коллекции Shapes
слайда (не забывайте, что коллекции индексируются с единицы, а не с нуля). Вот например как можно выставить текст заголовка:var title = s.Shapes[1];<br/>
title.TextFrame2.TextRange.Text = cff.ApplicationSettings.SlideTitle;<br/>
К слову замечу что использование ключевого слова
var
очень помогает при разработке. Что еще заметно, так это то, что при написании всего add-in’а мне не разу не пришлось использовать значение missing
, которое является одним из главных неудобств при работе с расширением офиса. В C#4 такое проблемы конечно же больше нет.Вот пример того, как манипулировать свойствами текствой рамки. В моем случае я выставляю заданный пользователем шрифт а также убираю дефолтные поля и «буллиты»:
var code = s.Shapes[2].TextFrame2.TextRange;<br/>
code.ParagraphFormat.FirstLineIndent = 0;<br/>
code.ParagraphFormat.Bullet.Visible = MsoTriState.msoFalse;<br/>
code.Font.CopyFrom(cff.ApplicationSettings.CodeFont);<br/>
code.ParagraphFormat.RightIndent = 0;<br/>
code.ParagraphFormat.LeftIndent = 0;<br/>
code.ParagraphFormat.SpaceBefore = 0;<br/>
Заметьте что для шрифтов используется метод расширения. Проблема в том, что Office использует свой тип
Font2
, а конверсия из System.Drawing.Font
конечно же не определена. Еще заметьте что в офисе, типы true
и false
– это enum’ы, чтобы можно было выставить значения «да», «нет» и «не задано». Опять же, нетрудно при желании определить конверсию из нашего bool
в офисный MsoTriState
.Работа с текстом
Как вы уже догадались, текст кладется в объект типа
TextFrame2
. Когда вы добавляете в рамку текст, вы получаете ссылку на тот отрезок кода, который добавили:var range = textarea.TextFrame2.TextRange.InsertAfter(finalText);<br/>
Теперь можно менять вкусовые качества этого отрезка текста – такие как шрифт или цвет:
range.Font.Fill.ForeColor.RGB = settings.EmphasisColor.ToRgb();<br/>
range.Font.CopyFrom(settings.EmphasisFont ?? settings.CodeFont);<br/>
Копирование шрифтов мы уже обсудили, а с цветом – та же проблема. Если использовать функцию
ToArgb()
которая уже встроена, то получите цвет «наизнанку». Пришлось реализовывать конверсию вручную:public static int ToRgb(this Color color)<br/>
{<br/>
return (color.A << 24) + (color.B << 16) + (color.G << 8) + color.R;<br/>
}<br/>
Тут пора бы уже и закончить, только вот хочу сделать одно предупреждение: если вы сделали параграф определенного цвета, то следующий параграф или кусочек кода будет такой же. Поэтому, когда строите текст из составных частей, сначала закэшируйте дефолтные значения, и применяйте их к каждому отрезку, который хотите добавить.
Заключение
Работа с инфраструктурой VSTO оказалась весьма несложной, и результаты использования add-in’а скоро можно будет увидеть на моих презентациях в Spbalt.net, Ineta, ActiveMesa и других организациях. А если вас вдруг заинтересовал проект, то результат работы add-in’а а также исходный код можно скачать тут.
Заметки
- ↑ Кстате, этот кусочек кода был использован для реализации паттерна Prototype в add-in’е. Хотя сейчас мне кажется что это перебор – можно было воспользоваться и
MemberwiseClone()
. - ↑ На самом деле – нет, нельзя. ИМХО, презентации нужно выкладывать в PDF и только в PDF (или вы думаете что пользователей Linux программирование не интересует?), а при конверсии в PDF анимация PP теряется. Поэтому подход, показаный тут, идеален для таких групп как Spbalt.net где материалы докладов только в PDF и выставляются.
- ↑ Первым расширением PP которое я написал была визуализация нейронных сетей. Это было 3 года назад, еще для 2003 офиса.
- ↑ Эту фразу (eating our own dogfood) любят использовать сотрудники Microsoft когда говорят про то, что используют свою же инфраструктуру для разработок.