めも

主にUnity

Word VSTOで文字列置換アドインを作る方法【ボタンで関数実行】

https://i.gyazo.com/9ff582f0dd054ded583e79f754497906.gif
https://i.gyazo.com/346201ffa60489715f5d2484fbd36e85.gif

Office(Word)のアドイン

アドインの実行方法による分類

正直何が違うのか全くわからない。全然違ってそう。
自分がVSTOを選んだのはVBが苦手でOfficeアドインが難しそうだったから…でもOfficeアドインも年々更新してるしネットの情報も少ないし散乱してるしVBAばっかだしもうよくわからん…これ以上はやめよう…

・マクロ

Office Visual Basic for Applications (VBA) リファレンス | Microsoft Docs
特定のドキュメントに付随し、そのドキュメントのみで使用可能。ただしテンプレートに予め登録しておくことが可能。VBで書くことを想定している。ライブラリを使って抜け道できるらしい?。
【UI】普通は開発タブのマクロ実行ボタンを押すことで実行することを想定している。よくわからん

・Officeアドイン/(VS表示名:Web アドイン)

Office アドイン プラットフォームの概要 - Office Add-ins | Microsoft Docs
すべてのドキュメントで使用可能。クロスプラットフォーム志向。Webっぽい?
【UI】リボン、作業ウィンドウ、文章内コントロールWPFなど

・COMアドイン・VSTOアドイン/(VS表示名:VSTO アドイン)

COMアドインとVSTOアドイン、多分ほとんど同じものを指していると思われるが…よくわからない。
Visual Studio を使用して Office 用 VSTO アドインを作成する | Microsoft Docs
すべてのドキュメントで実行可能。
【UI】Office アドインと同じ?

開発タブのアドイン関係の解説

f:id:mikomon:20201202125013j:plain

①マクロ

使用可能なマクロを設定するボタン。

②アドイン

Officeアドインを導入するボタン。

③Word アドイン

(マクロが入っていてもよい)テンプレート、XMLスキーマXML拡張パック、CSSを導入するボタン。

④COM アドイン

COMアドイン・VSTOアドインを導入するボタン。

詳しくは
「【ワテ解説】エクセルのアドイン【VBA、COM、EXEEL-DNA、XLLアドイン】」https://www.wareko.jp/blog/explanation-of-excels-add-in-vba-com-excel-dna-xll#toc2
「アドインの開発方法について | すばらしきOfficeとアドインの世界」
https://office-fun.com/develp-o-addin/
SharedScreenshot

制作手順

1.プロジェクト生成

Visual Studio起動 →ファイル(F)→プロジェクト(P)…→Visual C# Office/SharePoint/VSTOアドイン/Word 2013 と 2016 VSTO アドイン→(名前を設定)→OK
f:id:mikomon:20201202121201j:plain

2.実行タイミング

①ボタンで関数を実行させたい時

UI制作
プロジェクト(P)→新しい項目の追加…→リボン(ビジュアルなデザイナー)→(名前を設定)→追加(A)
f:id:mikomon:20201202121311j:plain
・左側のツールボックスからボタンを追加する。
・ボタンをダブルクリックする。すると、Ribbon1.csに

private void button1_Click(object sender, RibbonControlEventArgs e) {

        }

が出るので、
f:id:mikomon:20210121215829p:plain

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

とする。Globals.ThisAddinで実行中のアドインが参照される。余談だが、Globalsは実行中のリボンなど色々参照できるので、困ったときはとりあえずGlobalsとかけばあとはVSがなんとかしてくれると思う。(?)
f:id:mikomon:20210121215706p:plain

②自動で動作させたい時
        private void Application_WindowSelectionChange(Selection s) 

などデフォルトで関数が用意されているため、調べると良い。https://docs.microsoft.com/en-us/office/vba/api/word.application

3.関数

Func1をつくる。ThisAddIn.csのpublic partial class ThisAddin {}の中に、以下の関数を追加する。

        public void Func1() {

        }

f:id:mikomon:20210121215917p:plain
この内容を自由に決めれば良い。

4.文章の取得

ここでは、文字列置換アドインを作りたいのであった。wordの内部の文字列を取得する方法を記述する。

using Microsoft.Office.Interop.Word;
①選択部分
var sel = this.Application.Selection;
var rng = sel.rng;
②文章全体
var doc = this.Application.ActiveDocument;
var rng = doc.Content;

5.置換

void Replace(object before, object after) {
rng = doc.Content;
rng.Find.ClearFormatting();//Findの初期化
rng.Find.Forward = true;//前から探す
do {
f.Execute(
    before, ref missing, ref missing, ref missing, ref missing,
    ref missing, ref missing, ref missing, ref missing, after,
    WdReplace.wdReplaceOne, ref missing, ref missing, ref missing, ref missing);
} while (f.Found);
/*こっちは後述するUndoRecordがうまく行かないため非推奨(?)
rng.Find.Execute(
before, ref missing, ref missing, ref missing, ref missing,
ref missing, ref missing, ref missing, ref missing, after,
WdReplace.wdReplaceAll, ref missing, ref missing, ref missing, ref missing);*/
}

でできる。
f:id:mikomon:20210121220122p:plain

6.特殊文字ワイルドカード

WordではRegeX正規表現をそのまま使用できないはず。代わりにWord本体と同じ特殊文字ワイルドカードをそのまま使用できる。

特殊文字

特殊文字 入力される文字
段落記号 ^p
タブ文字 ^t
任意の1文字 ^?
任意の数字 ^#
任意の英字 ^$
キャレット ^^
セクションの文字 ^%
段落の文字 ^v
段区切り ^n
省略記号 ^i
3点リーダー ^j
全角ダッシュ ^+
1/4スペース ^q
半角ダッシュ ^=
任意指定の改行 ^x
改行なし ^z
文末脚注記号 ^e
フィールド ^d
脚注記号 ^f
グラフィック ^g
任意指定の行区切り ^l
任意指定のページ区切り ^m
改行をしないハイフン ^~
改行をしないスペース ^s
任意指定のハイフン ^-
セクション区切り ^b
全角または半角の空白 ^w

引用:特殊文字の検索や置換:Word(ワード)2013基本講座

7.探す

複雑な置換がしたい場合は、まず探す。

void FindInitialize(Range rng, string findtext) {
rng.Find.ClearFormatting();
rng.Find.Forward = true;
rng.Find.Text = findtext;
Execute(rng);
}

void Execute(Range rng) {
rng.Find.Execute(
ref missing, ref missing, ref missing, ref missing, ref missing,
ref missing, ref missing, ref missing, ref missing, ref missing,
ref missing, ref missing, ref missing, ref missing, ref missing);
}

Findの動作

方法: プログラムによって検索後に選択を復元する | Microsoft Docs
上の場合とは異なり、繰り返しを行う必要がある。Executeはこの場合、以下のような動作を行う。

  • FindInitialize()で初期化

繰り返し

  • Find.Execute()を実行する→Rangeは[Rangeの最初の文字より後ろ]にありかつ条件に合う文字列を探し、見つけたらrngはその文字列の部分になる。→Find.Foundがtrueになる
  • rngの情報をもとに任意の処理をする。

  • Find.Execute()を実行する→これ以降には見当たらない→Find.Foundがfalseになる。rngが勝手にExecuteする前は戻らないことに注意。
while (rng.Find.Found) {
//処理
Execute();
}

無限ループは起こらないから、安心してほしい。
//処理の中身は、例えば

var r = doc.Range(rng.Start-23, rng.End);
r.Font.Subscript = 1;//(下付き文字)
r.Delete(1,1);//(文字消去)
r.InsertAfter("miko");
r.InsertBefore("mikomiko");//(挿入)

などができる。
f:id:mikomon:20210121220603p:plain

8.「元に戻す」を変更する

デフォルトでは「元に戻す」には一つ一つの操作が記録されるため、一回の関数実行で多数の「元に戻す」記録が作られ、操作が面倒になる。そこで、

var objUndo = Application.UndoRecord;
objUndo.StartCustomRecord("操作名");
//処理 
objUndo.EndCustomRecord();

というように囲むことで「元に戻す」を一回にできる。
UndoRecord オブジェクトを使用する | Microsoft Docs
ただしwdReplaceAllを使った置換があると失敗するので注意。Wordが強制終了したときは何事かと焦った。
vba - VBA-Wordのバグ:Undoリストに「Apply Quick Style」があるときにUndoRecordを使用して「Replace All」を記録すると、Undoリストでエラーが発生します。回避する方法は? - ITツールウェブ

9.ボタンを萌え絵にする

著作権的に自作のあまりきれいじゃないアイコンで例を示す。お目汚し申し訳ない。
Ribbon1.cs[デザイン]→(button1をクリック)→右下のプロパティ
各種設定変更
f:id:mikomon:20201201180549j:plain
before→After
f:id:mikomon:20201201180553j:plain
さらに、ボタン画像は動的に変更できる。
なおリボンの仕様でどうしてもこの大きさ以上にはできない。Word使用中にもっと大きい画像を表示させたいという場合は、上記の手順と同様にして関数をつくり、萌え絵を全面に張り出したFormを呼び出すとか、作業ウィンドウを導入するなどしかないと思われる。

おつかれさまでした。