空談録

世界で5人くらいに役立ちたい

今更だけどVSTOアドインを作ってみよう

この記事は Office アドイン Advent Calendar 2015 - Adventar の7日目を埋めるための記事です
特に埋める気はないので24個登録とかはしていません
1日目で頓挫すればみんな埋めることを気にしなくて済む(意味不明)

まあそんなこんなですが、Morphやばい!って言いまくってたらネタがないです。なのでVSTOでアドインでも作っておきましょう
初心者なので初心者らしくスライドをコピーして後ろにペーストするアドインを作ります
Morphでコピペするのが面倒ですからね

というわけで早速作りましょう

ソリューションを作ろう

Visual Studioを立ち上げます。プロジェクトの新規作成で好きな言語(VB/C#)の中にあるOffice/SharePointからPowerPoint向けのアドインが作れそうなのを選んで作成しましょう
こんな感じ
f:id:fantasticswallow:20151207180210p:plain

ん? Excelじゃないかって?  違うんや…これは重大な事故だったんや…オブジェクトブラウザ開いてSlideがなくて気が付いた…
とりあえず作ったら進みましょう

リボンを追加しよう

ボタンが欲しいのでリボンを追加しましょう
新しい項目の追加から「リボン (ビジュアルなデザイナー)」を選んで追加します
今回はやる気が足りないのでそのままTabAddinsのGroup1のまま行きましょう
位置を指定する場合はOffice Fluent User Interface Control Identifiersっていう資料を読みましょう
Download Office 2013 Help Files: Office Fluent User Interface Control Identifiers from Official Microsoft Download Center
2013向けは上のリンクから。2016向けはまだないです。BeforeとAfterが指定できるのでおそらく困ることはないはず

とりあえず作り終えた結果がこれ
f:id:fantasticswallow:20151207180921p:plain
芸術的やる気のなさを感じますね

追加したボタンをダブルクリックしてイベントハンドラを追加しときます。そこまでやったらいったんThisAddinに戻りましょう

スライドのコピペをするコードを書く

ボタンを押した処理を書こうにもコピペする処理が必要ですね
しかしOffice開発なんかしたこともないしコピペするコードなんて思いつきません。どうしましょう

まあ2択ですが
・ヘルプめっちゃ見る
ググる
があります。まあどっちにしろググるのですが上は「関数の詳細を見る」で下は「キーワードからコードのサンプルを探す」みたいなイメージで行きます

今回は上で行きます。キーワードがわからないのでオブジェクトブラウザを見ましょう
目的は「スライドのコピー」なのでSlideとかPresentationとかのオブジェクトにCopyとかPasteみたいなのがあるか探します
すると"SlideClass.Copy()"っていう関数が見つかります。しかしSlideClass.Pasteがありません。ぐぬぬ

Pasteがなくて使い方がよくわからないので"PowerPoint.Slide.Copy MSDN"とか入れてぐぐーる先生に聞きましょう
すると次のページが一番上に来ます
Slide.Duplicate Method (PowerPoint)
Summaryが"Creates a duplicate of the specified Slide object, adds the new slide to the Slides collection immediately after the slide specified originally, and then returns a Slide object that represents the duplicate slide."となっています
めっちゃ簡単に書くと「そのスライドの複製を一つ後ろに作るよ」って書いてあります
さすがぐぐーる先生、ほしい関数を一発で持ってきてくれる

もう一つ必要なことがあります。現在のスライドの位置を取得するコードがわかりません
これはさすがにオブジェクトブラウザであたりをつけるのが難しいのでキーワードで検索してみましょう
"PowerPoint active slide index"とかでググると2番目に次のようなページが出てきます
PowerPoint VBA: Getting an Active Slide Number | Beyond VBA Tutorial
さすがVBA、ググれば何でも出てくる

Selectionを使ってもにょもにょするといいと学んだのでようやくC#のコードを書きます
ThisAddinに次のコードを追加します

internal static void CurrentSlideDuplicate()
{
    var idx = Globals.ThisAddIn.Application.ActiveWindow.Selection.SlideRange.SlideIndex;
    Globals.ThisAddIn.Application.ActivePresentation.Slides[idx].Duplicate();
}

VSTOではGlobalsというクラスが利用できます。こいつなしではApplicationへの参照をStartUpで取得するみたいなクソコードが生まれるので素晴らしいクラスです
GlobalsからApplicationを参照する場合ThisAddin.Applicationで行きます

これで現在のスライドの複製ができるはずです。ボタンのクリック時の動作も追加します

private void button1_Click(object sender, RibbonControlEventArgs e)
{
    ThisAddIn.CurrentSlideDuplicate();
}

こっちは呼ぶだけですね
ここまでやったらデバッグしてみましょう

f:id:fantasticswallow:20151207183237p:plain

この状態でボタンを押すと

f:id:fantasticswallow:20151207183258p:plain

ちゃんと複製されていますね

ちなみにさっきのコードだと複製先のスライドが選択されません。なのでThisAddinのコードを書き換えて選択するようにしましょう。

internal static void CurrentSlideDuplicate()
{
internal static void CurrentSlideDuplicate()
{
    var idx = Globals.ThisAddIn.Application.ActiveWindow.Selection.SlideRange.SlideInde
    // Globals.ThisAddIn.Application.ActivePresentation.Slides[idx].Duplicate();
    var sld = Globals.ThisAddIn.Application.ActivePresentation.Slides[idx].Duplicate();
    sld.Select();
}

Duplicateの返り値は複製したスライドなので、そのスライドをSelectすればフォーカスしてくれます。Office界ではFocusするときはSelectって書いとけばいいです

ここで終わると面白くないのでもうちょっと頑張ります

Morphアニメーションを複製したスライドに追加する

というわけでコピペしたスライドにMorphのアニメーションが適用済みだとさらにうれしいですよね。ぜひ追加しましょう

MorphのアニメーションがPpEntryEffect.ppEffectMorphBy{Hoge}であることはすでに周知の事実ですので(ググっても公式すら出ないけど)
・PpEntryEffectのスライドへの適用方法
・ppEffectMorphByObjectの値を調べる
ことが必要となります

まずはPpEntryEffectのほうから調べましょう。このとき最初のPpはPowerPointの略であり、列挙子の名前の接頭辞として使われています。しかし列挙子の接頭辞はプロパティなどでは外れます。Office開発では重要な知識ですね
今回であれば、PpEntryEffectを調べる場合EntryEffectという名前のプロパティを探す必要があります。列挙子のプロパティは基本このパターンなので安心してください

EntryEffectで調べると"SlideShowTransition.EntryEffect"と"AnimationSettings.EntryEffect"が出てきます。Morphは遷移アニメーションなのでとりあえずSlideShowTransitionのほうを見ましょう
MSDNの記事はこちら
SlideShowTransition.EntryEffect プロパティ (PowerPoint)

…見ましたか? 見てしまいましたか? これがOffice開発だ!
何がすごいってこれ注釈以下がAnimationSettingsの内容なんですよ。だからTextLevelEffectとかいうプロパティは左にないし、Animatedプロパティをtrueにする必要もありません

とりあえずOffice開発はクソと言いながらSlideShowTransitionであることを覚えておきます。次にppEffectMorphByObjectの値を調べます
というのもVSTOで2016向けアドインは作れないのです。どういう意味かって? オブジェクトブラウザなりで出るデータは2013時点のものってことですよ
(まあそもそもOffice Insider向けでしか出てないバージョンなので2016向けに作れてもあるとは思えませんが)

何にせよないのです。調べます
PowerPointを開いてAlt+F11→F2って押すと偶然にもVBA版オブジェクトブラウザが出てきます。ここでppEffectMorphByObjectで検索した結果がこれ
f:id:fantasticswallow:20151207185037p:plain

値が3954であることがわかります。これを使って書きましょう

上のコードに書き足すとこんな感じ

internal static void CurrentSlideDuplicate()
{
    var idx = Globals.ThisAddIn.Application.ActiveWindow.Selection.SlideRange.SlideIndex;
    // Globals.ThisAddIn.Application.ActivePresentation.Slides[idx].Duplicate();
    var sld = Globals.ThisAddIn.Application.ActivePresentation.Slides[idx].Duplicate();
    sld.Select();
    if (powerPointBuild >= 6366 && powerpointVersion.CompareTo(powerpoint16Version) >= 0)
    {
        sld.SlideShowTransition.EntryEffect = (PowerPoint.PpEntryEffect)3954;
    }
}

Slide.SlideShowTransition.EntryEffectに3954をPpEntryEffectにキャストしたものを突っ込みます。当然ですがインテリセンスも聞かないのでppEffectMorphだとか書いても動きません
また、これを実行する際にバージョンを厳密にしないと危険なのでifでくくっています。プロパティ周りはStartUpで設定してます。こんなコード

private static Version powerpointVersion { get; set; }
private static readonly Version powerpoint16Version = new Version("16.0");
private static int powerPointBuild { get; set; }

private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
    powerpointVersion = new Version(Application.Version);
    powerPointBuild = int.Parse(Application.Build);
}

Application.Versionが"16.0"とか"15.0"みたいにメジャーバージョンとマイナーバージョンしかくれないのでこんな感じに。ビルド番号はApplication.Buildで取得。stringなので要変換
あとはcompareToしたりして"16.0.6366"よりあとだったら実行しよう、ということをしています

これで実行するとちゃんと複製後のスライドにMorphのアニメーションが追加されます。やったね!スライド作成がはかどるよ!


ということで今回は完成ということにしときます。プロパティ一切いじってないけどまあ書くことでもないし…
コンテキストメニューにしたい!っていう話は来週しましょう

来週は何もなければCustomTaskPaneについて調べたいという願望だけあります。何もしてません
戦車道にはまってなければきっと大丈夫…

この辺で