VBAからGDI+を使う資料集
inet上の達人の方々から御教示いただいたコードを少しアレンジさせてもらっています(切り貼りとも言います)。
  1. ホーム
  2. ribbon
  3. skeleton


独自リボン作成のスケルトン

久しぶりにRibbonを触ってみて、Objectとして取り扱えないのに戸惑いました。
http://msdn.microsoft.com/ja-jp/library/bb194905(v=office.12).aspx
こちらの記事に、
「リボン UI を初めて見た Office 開発者が真っ先に探すのは、CustomRibbonUI.Items のような要素でしょう。
この考え方はもう通用しません。IRibbonUI を返す onLoad コールバックを見つけると、だれもが大喜びします。
しかし、IRibbonUI タイプが Invalidate() および InvalidateControl(string) の 2 つのメソッドしか持たず、
しかも、リボン項目のコレクションへのアクセスをまったく提供しないことに気付くと、その喜びは終わってしまいます。
リボン UI では、コールバックとユーザーの "プル" にすべてが依存しています。」
とあります。
・コードからRibbon上のコントロールの値を直に取得できず、コントロールのコールバック関数でモジュールレベル変数などに保存してから使う必要があります。
・コードからRibbon上のコントロールの値を直に設定できず、モジュールレベル変数に設定後、コントロールのget系のコールバックに渡し、
 当該コントロールもしくは設定する範囲により、Ribbon全体をInvalidateする必要がある様です。
上記の記事に、「簡単な抽象型によって、...MyRibbonButton1.Visible=true というテクニックです。」とありますが、これは専用のクラスを作成して、 Objectと同様にアクセスできる様にするという事みたいです。VBAで同様の事が出来るかどうかは不明です。

と、いう訳で、備忘録として独自Ribbonを作成するためのskeletonを作成してみました。



skeleton.xlsm.zip をエクスプローラーでみてみると

skeleton.xlsm.zip
	- _rels
	- customUI
		- customUI14.xml
	- docProps
	- xl

というフォルダー構成になっています。援用ツールに頼らず、一から自分でやるにはcustomUIフォルダと、中味のxmlを追加する必要があります。

また、_relsフォルダー内の.relsファイルに、リボンの設定ファイルの在処を示す一行を追加してやる必要があります。
_rels\.rels の中味は、Office Ribbon Editorで生成されたものでは、下記の様になっていました。
これは、そのままオリジナルと入れ替えて使用できます。
Id="R11de75333ffa4be1"と、自動生成された妙なIdになっている行がポイントです。Idは真っ当な任意の文字列(当然他とバッティングしない様に)と入れ替え可です。

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
<Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties" Target="docProps/app.xml"/>
<Relationship Id="R11de75333ffa4be1" Type="http://schemas.microsoft.com/office/2007/relationships/ui/extensibility" Target="customUI/customUI14.xml"/>
<Relationship Id="rId2" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="docProps/core.xml"/>
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/>
</Relationships>

さて、本題のskeletonのXMLおよびVBAのコードです

'☆CustomUI14.xml
<!-- CustomUI -->
<!-- onLoad でリボンのオブジェクト変数への取得および初期化プロシージャの実行を指定 -->
<customUI xmlns="http://schemas.microsoft.com/office/2009/07/customui"  onLoad="OnLoad">

<!-- Ribbon -->
	<ribbon startFromScratch="false">
		<tabs>
			<tab id="CustomTab" label="My Tab">
				<group id="SampleGroup" label="Sample Group">
					<button id="group1Button1" imageMso="TableEffectsCellBevelGallery" label="group1Button1" size="large" onAction="group1Button_click" />
					<checkBox id="group1CheckBox1" label="CheckBox1" 
            screentip="This is a check box" 
            onAction="group1CheckBox1_click" getPressed="group1CheckBox1_GetPressed" />
					<checkBox id="group1CheckBox2" label="CheckBox2" 
            screentip="This is a check box" 
            onAction="group1CheckBox2_click" getPressed="group1CheckBox2_GetPressed" />
					<button id="group1Button2" imageMso="TableEffectsCellBevelGallery" label="group1Button2" size="large" onAction="group1Button_click" />
					<editBox id="EditBox1" getText="EditBox1_getText" label="My EditBox" onChange="EditBox1_change"/>
				</group>
			</tab>
		</tabs>
	</ribbon>

<!-- ContextMenu -->
<contextMenus>
	<contextMenu idMso="ContextMenuCell">
		<button id="contextMenuButton1" label="Function1" onAction="cmButton_Click" />
<button id="contextMenuButton2" label="Function2" onAction="cmButton_Click" />

	</contextMenu>
</contextMenus>
	
</customUI>


'☆標準モジュール
'ribbonCallBack Module
'注)複数のコントロールでget系のCallBack関数を共有する場合、Invalidateにより、コントロールの個数分実行される事に注意。


Private rbRibbon As IRibbonUI ' リボンを保持するオブジェクト変数

'値を取得するコールバックに渡すために利用するモジュールレベル変数

Private checkBox1Value As Boolean
Private checkBox2Value As Boolean


'ここにcustomUI(リボンを含む)が読み込まれる時に実行する処理を記述する
'1.後で利用するためにリボンをオブジェクト変数に保存する
'2.モジュールレベル変数の初期化
'Callback for customUI.onLoad

Sub OnLoad(ribbon As IRibbonUI)
   Set rbRibbon = ribbon ' リボンの表示を更新できるようにするためにリボンをオブジェクト変数にセットする
  
  'ModuleLevel変数の初期化
  checkBox1Value = True
  checkBox2Value = False
  
  rbRibbon.Invalidate ' リボンの表示を更新する
End Sub

'Callback for group1Button1 onAction
Sub group1Button_click(control As IRibbonControl)
  Select Case control.ID
    Case "group1Button1"
      MsgBox "CheckBox1は" & checkBox1Value & "です"
    Case "group1Button2"
      MsgBox "CheckBox2は" & checkBox2Value & "です"
  End Select
End Sub

'Callback for group1CheckBox1 onAction
'CheckBoxがクリックされた時呼び出されるCallBack、クリック前と値が反転している事に注意
Sub group1CheckBox1_click(control As IRibbonControl, pressed As Boolean)
  'If Checked
  If pressed Then
    checkBox1Value = True
    checkBox2Value = False
  Else
    checkBox1Value = False
    checkBox2Value = True
  End If
  'Ribbonの更新
  rbRibbon.Invalidate
End Sub

'Callback for group1CheckBox1 getPressed
'Invalidateされた時に、設定値を読み込む
Sub group1CheckBox1_GetPressed(control As IRibbonControl, ByRef returnedVal)
  returnedVal = checkBox1Value
End Sub

'Callback for group1CheckBox2 onAction
'CheckBoxがクリックされた時呼び出されるCallBack、クリック前と値が反転している事に注意
Sub group1CheckBox2_click(control As IRibbonControl, pressed As Boolean)
  'If Checked
  If pressed Then
    checkBox1Value = False
    checkBox2Value = True
  Else
    checkBox1Value = True
    checkBox2Value = False
  End If
  'Ribbonの更新
  rbRibbon.Invalidate
End Sub

'Callback for group1CheckBox2 getPressed
'Invalidateされた時に、設定値を読み込む
Sub group1CheckBox2_GetPressed(control As IRibbonControl, ByRef returnedVal)
  returnedVal = checkBox2Value
End Sub

'初期値設定用
Sub EditBox1_getText(control As IRibbonControl, ByRef text)
  text = "input number"
End Sub

'Callback for EditBox1 change
Sub EditBox1_change(control As IRibbonControl, text As String)
  Dim myNumber As Double
  
  On Error Resume Next
  myNumber = CDbl(text)
  If Err.Number <> 0 Then
    MsgBox "数値を入力して下さい"
    text = "input number"
    rbRibbon.InvalidateControl control.ID
  End If
End Sub

'Callback for contextMenuButton1 onAction
Sub cmButton_Click(control As IRibbonControl)
  Select Case control.ID
  
    Case "contextMenuButton1"
      MsgBox "右クリックメニュー1"
    Case "contextMenuButton2"
      MsgBox "右クリックメニュー2"
  End Select
End Sub