【備忘録】メッセージボックスのボタン表示を無理矢理変える手順(VB.NET)
どうも、りょーちんです。
タイトルの通りです。まぁ、業務上でハマった内容を備忘録として残しておくことにします。
経緯:VB.NETでResourceを使用して多言語対応を行った業務プログラムを取り扱っています。メッセージボックスに表示するメッセージ内容については、言語設定を変更することで、Resourceファイルから対象言語のメッセージをIDから引っ張って表示する仕組みで対応できています。
しかし、
メッセージボックスのボタンだけなぜか日本語のまま…。
「はい(Y)」「いいえ(N)」のやつ。
なぜなら、WindowsFormsのボタンをそのまま利用しているためです。
英語OSを使用すれば、「Yes(Y)」「No(N)」になるはず。
日本語OSなら当然日本語になりますよね。
そこで、違和感あるからなんとかならんか?と言われてしまい…。
(正直英語OSで切り替わるならいいじゃん…。mndks…。)
まぁ、Googleさんで検索してみました。
見つけた方法は2つ。
- 新規フォームからメッセージボックスを作成する。
- フックを使う。
1は、確実な方法です。
ただ、既存にメッセージボックスの記述部分が多数存在する場合、書き換えるのに手間がかかってしまう難点があります。
今回は、2の方法で対応してみます。
C#やC+の記述例はヒットしますが、VB.NETの記述はヒットしなかったので、
以下、サンプルを記載します。(やろうと思えばC#やC+にも利用可)
'【CustomMsgBox.vb】 Imports System.Text Imports System.Drawing Imports System.Windows.Forms Imports System.Runtime.InteropServices Public Class CustomMsgBox Private hHook As IntPtr = IntPtr.Zero Public Property ButtonText As CustomButtonText Public Sub New() Me.ButtonText = New CustomButtonText() End Sub Public Function Show(ByVal text As String, ByVal caption As String, ByVal buttons As MessageBoxButtons, ByVal icons As MessageBoxIcon) As DialogResult Try BeginHook() Return MessageBox.Show(text, caption, buttons, icons) Finally EndHook() End Try End Function Private Sub BeginHook() EndHook() Me.hHook = SetWindowsHookEx(WH_CBT, New HOOKPROC(AddressOf Me.HookProcs), IntPtr.Zero, GetCurrentThreadId()) End Sub Private Function HookProcs(ByVal nCode As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As IntPtr If nCode = HCBT_ACTIVATE Then If Me.ButtonText.Abort IsNot Nothing Then SetDlgItemText(wParam, ID_BUT_ABORT, Me.ButtonText.Abort) End If If Me.ButtonText.Cancel IsNot Nothing Then SetDlgItemText(wParam, ID_BUT_CANCEL, Me.ButtonText.Cancel) End If If Me.ButtonText.Ignore IsNot Nothing Then SetDlgItemText(wParam, ID_BUT_IGNORE, Me.ButtonText.Ignore) End If If Me.ButtonText.No IsNot Nothing Then SetDlgItemText(wParam, ID_BUT_NO, Me.ButtonText.No) End If If Me.ButtonText.OK IsNot Nothing Then SetDlgItemText(wParam, ID_BUT_OK, Me.ButtonText.OK) End If If Me.ButtonText.Retry IsNot Nothing Then SetDlgItemText(wParam, ID_BUT_RETRY, Me.ButtonText.Retry) End If If Me.ButtonText.Yes IsNot Nothing Then SetDlgItemText(wParam, ID_BUT_YES, Me.ButtonText.Yes) End If EndHook() End If Return CallNextHookEx(Me.hHook, nCode, wParam, lParam) End Function Private Sub EndHook() If Me.hHook <> IntPtr.Zero Then UnhookWindowsHookEx(Me.hHook) Me.hHook = IntPtr.Zero End If End Sub Public Class CustomButtonText Public Property OK As String Public Property Cancel As String Public Property Abort As String Public Property Retry As String Public Property Ignore As String Public Property Yes As String Public Property No As String End Class Const WH_CBT As Integer = 5 Const HCBT_ACTIVATE As Integer = 5 Const ID_BUT_OK As Integer = 1 Const ID_BUT_CANCEL As Integer = 2 Const ID_BUT_ABORT As Integer = 3 Const ID_BUT_RETRY As Integer = 4 Const ID_BUT_IGNORE As Integer = 5 Const ID_BUT_YES As Integer = 6 Const ID_BUT_NO As Integer = 7 Private Delegate Function HOOKPROC(ByVal nCode As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As IntPtr <DllImport("user32.dll")> Private Shared Function SetWindowsHookEx(ByVal idHook As Integer, ByVal lpfn As HOOKPROC, ByVal hInstance As IntPtr, ByVal threadId As IntPtr) As IntPtr End Function <DllImport("user32.dll")> Private Shared Function UnhookWindowsHookEx(ByVal hHook As IntPtr) As Boolean End Function <DllImport("user32.dll")> Private Shared Function CallNextHookEx(ByVal hHook As IntPtr, ByVal nCode As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As IntPtr End Function <DllImport("kernel32.dll")> Private Shared Function GetCurrentThreadId() As IntPtr End Function <DllImport("user32.dll", CharSet:=CharSet.Auto)> Private Shared Function SetDlgItemText(ByVal hWnd As IntPtr, ByVal nIDDlgItem As Integer, ByVal lpString As String) As Boolean End Function End Class
'【Sample.vb】 Dim msg As New CustomMsgBox '▼「はい」を「イエっ俊龍」に設定 msg.ButtonText.Yes = "イエっ俊龍" '▼「いいえ」を「ノー俊龍」に設定 msg.ButtonText.No = "ノー俊龍" '▼「キャンセル」を「メンブレしたのでキャンセル」に設定 msg.ButtonText.Cancel = "メンブレしたのでキャンセル" '▼メッセージ表示 msg.Show("LINEをやりますか?", "確認", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question)
メリットは、新たにフォームを作らずに記述を少々いじる程度で対応ができること。
デメリットは、フックを利用することがあまり推奨されていないことから、問題があった時に対応に困りそう。
※フックの解除のし忘れには要注意らしい。下手すればOS再インストールとか怖い書き込みありましたが…。
あと無理矢理ボタンの表示名を変えているからか、標準のショートカットキー(「はい(Y)」ならYキー、「いいえ」ならNキー)が
使えなくなるところがネックに感じますね。
なんかもっと良い方法があればいいものの…。(あればコメントに是非ご教授下さい。)
以上、備忘録でした。