Legendas de botões personalizados na caixa de mensagens .NET?
-
04-07-2019 - |
Pergunta
Existe uma maneira fácil de exibir uma caixa de mensagens em VB.NET com legendas de botões personalizadas?eu me deparei Qual é uma maneira fácil de criar um MessageBox com texto de botão personalizado em Managed C++?, nos arquivos Stack Overflow, mas é para C++ gerenciado.
Solução
Não, não há método para acessar ou redirecionar o texto do botão padrão do MessageBox.
A única maneira de fazer isso é codificar o seu próprio ou apenas usar um dos muitos gratuitos da Internet:
Outras dicas
Não.
Você terá que fazer um formulário personalizado com FormBorderType = FixedDialog
.
Aqui está um pequeno tutorial:
Criação de caixas de diálogo em .NET
Por James D. Murray em 12 de junho de 2007, abaixo de 70-526
Exame de certificação da Microsoft: 70-526 (MCTS)
Objetivo: Crie e use caixas de diálogo personalizadas nos aplicativos do Windows Forms.
Idioma: Visual Basic 2005 (clique aqui para a versão C# desta entrada)
Lembro -me da primeira vez que precisava criar uma caixa de diálogo em um aplicativo .NET que estava escrevendo em C#. Sendo um programador Visual Basic de longa data, assumi que isso poderia ser facilmente realizado usando um modelo de caixa de diálogo incluído no Visual Studio.net. Para minha surpresa, esse modelo de formulário existia para C#, embora um exista para o Visual Basic 2005. Depois de percorrer vários livros e páginas da web cheios de informações sobre a programação do Windows Forms 2.0, um conjunto básico de etapas tornou -se aparente para a conversão manual de um manualmente .NET FORM em uma caixa de diálogo Windows:
Etapa 1: Adicione um formulário ao seu projeto .NET e nomeie -o como "DIALOGBOXFORM".
Etapa 2: solte dois botões na área inferior direita do formulário e nomeie-os como "Okbutton" e "CancelButton".
Etapa 3: Altere as seguintes propriedades do formulário para ajustar sua aparência e comportamento como uma caixa de diálogo padrão:
Property Value Description ----------------------------------------------------------------------------------------------------------------------------- AcceptButton OK button instance Causes form to return value DialogResult.OK. Only used on modal dialog boxes. CancelButton Cancel button instance Causes form to return value DialogResult.Cancel. Only used on modal dialog boxes. FormBorderStyle FixedDialog Create a non-sizable form with no control box on the title bar. HelpButton True The Help button appears in the caption bar next to the Close button. The ControlBox property must be True for these buttons to be visible. MaximizeBox False Hide the Maximize button in the title bar. MinimizeBox False Hide the Minimize button in the title bar. ShowIcon False The title bar icon is not visible in a dialog box. ShowInTaskBar False Do not indicate the presence of the form on the Windows Task Bar. Start Position CenterParent The initial position of a dialog box is over its parent form. Size As Needed The fixed size needed for the dialog box.
Essas propriedades podem ser definidas usando a janela Propriedades para o formulário ou usando o código colocado no evento de carga do formulário:
Me.AcceptButton = OKButton
Me.CancelButton = CancelButton
Me.FormBorderStyle = Windows.Forms.FormBorderStyle.FixedDialog
Me.HelpButton = True
Me.MaximizeBox = False
Me.MinimizeBox = False
Me.ShowInTaskbar = False
Me.ShowIcon = False
Me.StartPosition = FormStartPosition.CenterParent
Etapa 4: Adicione o seguinte botão, clique em Manipuladores de eventos ao formulário:
Private Sub OKButton_Click(ByVal sender As Object, _ByVal e As EventArgs) ' User clicked the OK button Me.DialogResult = Windows.Forms.DialogResult.OK End Sub Private Sub CancelButton_Click(ByVal sender As Object, _ByVal e As EventArgs) ' User clicked the Cancel button Me.DialogResult = Windows.Forms.DialogResult.Cancel End Sub
Etapa 5: adicione propriedades que você precisa mover os dados para dentro e fora da caixa de diálogo, como faria em qualquer forma:
Private _LoginName As String Private _LoginPassword As String Public Property LoginName() As String Get Return _LoginName End Get Set(ByVal value As String) _LoginName = value End Set End Property Public Property LoginPassword() As String Get Return _LoginPassword End Get Set(ByVal value As String) _LoginPassword = value End Set End Property
Etapa 6: mostre a caixa de diálogo modalmente chamando o showDialog () do formulário:
Public Sub ShowDialogBox() Dim dialog As New DialogBoxForm dialog.LoginName = "JDMurray" dialog.LoginPassword = String.Empty If dialog.ShowDialog() = Windows.Forms.DialogResult.OK Then Debug.WriteLine("Login Name: " & dialog.LoginName) Debug.WriteLine("Password: " & dialog.LoginPassword) Else ' User clicked the Cancel button End If End Sub
Etapa 7: para mostrar a caixa de diálogo modificadamente, ligue para o método show () de DialogBoxform. Você precisará adicionar um manipulador de eventos ao evento próximo do DialogBoxform para saber quando o usuário fechar a caixa de diálogo:
Public Sub ShowDialogBox() Dim dialog As DialogBoxForm = New DialogBoxForm dialog.LoginName = "JDMurray" dialog.Password = String.Empty AddHandler dialog.FormClosed, AddressOf dialog_FormClosed dialog.Show() ' The Show() method returns immediately End Sub Private Sub dialog_FormClosed(ByVal sender As Object, _ ByVal e As FormClosedEventArgs) ' This method is called when the user closes the dialog box End Sub
MessageBox usa uma janela simples que pode ser alterada como qualquer outra janela.Isso é possível no Windows há muito tempo, já há mais de 20 anos.As técnicas estão ficando obscuras, muitos wrappers de classe amigáveis que escondem o winapi nativo e não expõem tudo o que você pode fazer com ele.Tanto é verdade que os programadores agora assumem automaticamente que isso não é possível, como você pode perceber pelas respostas votadas.É o tipo de programação que Petzold nos ensinou em seu livro seminal “Programming Windows”.Substituir MessageBox por um formulário ou janela personalizado é bastante difícil de fazer, ele faz um layout automático não trivial para caber no texto e oferece suporte à localização sem ajuda.Embora seja exatamente disso que você não parece gostar :)
De qualquer forma, a janela da caixa de mensagem é fácil de encontrar.Ele pertence ao thread da UI e possui um nome de classe especial que o torna único.EnumThreadWindows() enumera as janelas pertencentes a um thread, GetClassName() permite verificar o tipo de janela.Em seguida, basta inserir o texto no botão com SetWindowText().
Adicione uma nova classe ao seu projeto e cole o código mostrado abaixo.Invoque-o com um código como este:
Nobugz.PatchMsgBox(New String() {"Da", "Njet"})
MsgBox("gack", MsgBoxStyle.YesNo)
Aqui está o código:
Imports System.Text
Imports System.Runtime.InteropServices
Public Class Nobugz
Private Shared mLabels() As String '' Desired new labels
Private Shared mLabelIndex As Integer '' Next caption to update
Public Shared Sub PatchMsgBox(ByVal labels() As String)
''--- Updates message box buttons
mLabels = labels
Application.OpenForms(0).BeginInvoke(New FindWindowDelegate(AddressOf FindMsgBox), GetCurrentThreadId())
End Sub
Private Shared Sub FindMsgBox(ByVal tid As Integer)
''--- Enumerate the windows owned by the UI thread
EnumThreadWindows(tid, AddressOf EnumWindow, IntPtr.Zero)
End Sub
Private Shared Function EnumWindow(ByVal hWnd As IntPtr, ByVal lp As IntPtr) As Boolean
''--- Is this the message box?
Dim sb As New StringBuilder(256)
GetClassName(hWnd, sb, sb.Capacity)
If sb.ToString() <> "#32770" Then Return True
''--- Got it, now find the buttons
mLabelIndex = 0
EnumChildWindows(hWnd, AddressOf FindButtons, IntPtr.Zero)
Return False
End Function
Private Shared Function FindButtons(ByVal hWnd As IntPtr, ByVal lp As IntPtr) As Boolean
Dim sb As New StringBuilder(256)
GetClassName(hWnd, sb, sb.Capacity)
If sb.ToString() = "Button" And mLabelIndex <= UBound(mLabels) Then
''--- Got one, update text
SetWindowText(hWnd, mLabels(mLabelIndex))
mLabelIndex += 1
End If
Return True
End Function
''--- P/Invoke declarations
Private Delegate Sub FindWindowDelegate(ByVal tid As Integer)
Private Delegate Function EnumWindowDelegate(ByVal hWnd As IntPtr, ByVal lp As IntPtr) As Boolean
Private Declare Auto Function EnumThreadWindows Lib "user32.dll" (ByVal tid As Integer, ByVal callback As EnumWindowDelegate, ByVal lp As IntPtr) As Boolean
Private Declare Auto Function EnumChildWindows Lib "user32.dll" (ByVal hWnd As IntPtr, ByVal callback As EnumWindowDelegate, ByVal lp As IntPtr) As Boolean
Private Declare Auto Function GetClassName Lib "user32.dll" (ByVal hWnd As IntPtr, ByVal name As StringBuilder, ByVal maxlen As Integer) As Integer
Private Declare Auto Function GetCurrentThreadId Lib "kernel32.dll" () As Integer
Private Declare Auto Function SetWindowText Lib "user32.dll" (ByVal hWnd As IntPtr, ByVal text As String) As Boolean
End Class
Existe uma solução. Através da instalação de um gancho de CBT, é possível ajustar uma ampla variedade de configurações visuais da caixa de mensagens na mosca: fontes de mensagem e botão, fundo de diálogo, posicionamento de diálogo, ícones, legendas de botão, tempo limite e inserindo até controles adicionais.
Solução completa: MessageBox.http://www.news2news.com/vfp/?solution=5
É uma versão de avaliação totalmente funcional, a versão regular inclui o código-fonte C# completo.
Código C# para realizar a mesma coisa pode ser encontrada em um artigo no fórum MSDN, https://forums.microsoft.com/msdn/showpost.aspx?postid=3087899&siteid=1.
Adicione isso ao botão do qual deseja que a caixa de diálogo seja mostrada. Esta é uma caixa de mensagem de formulário personalizada;
private void DGroup_Click(object sender, EventArgs e)
{
messageBox m = new messageBox();
m.ShowDialog();
if (m.DialogResult == DialogResult.Yes)
{
//del(groups.php?opt=del&id=613','asdasd');
String[] asd = new String[2];
asd[0] = "groups.php?opt=del&id=613";
asd[1] = "asdasd";
addgroup.Document.InvokeScript("del",asd);
}
else
if (m.DialogResult == DialogResult.No)
{
MessageBox.Show("App won´t close");
}
}
Adicione este código à MessageBox.
private void deleteGroupOnly_Click(object sender, EventArgs e)
{
this.DialogResult = DialogResult.Yes;
this.Close();
}
private void deleteAll_Click(object sender, EventArgs e)
{
this.DialogResult = DialogResult.No;
this.Close();
}
private void cancel_Click(object sender, EventArgs e)
{
this.DialogResult = DialogResult.Cancel;
this.Close();
}
A solução de Daniel Nolan, código em vb.net
<DllImport("kernel32.dll")> _
Private Shared Function GetCurrentThreadId() As UInteger
End Function
<DllImport("user32.dll", CharSet:=CharSet.Auto)> _
Private Shared Function CallNextHookEx(ByVal idHook As Integer, ByVal nCode As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As Integer
End Function
<DllImport("user32.dll", CharSet:=CharSet.Auto)> _
Private Shared Function UnhookWindowsHookEx(ByVal idHook As Integer) As Boolean
End Function
<DllImport("user32.dll", CharSet:=CharSet.Auto)> _
Private Shared Function SetWindowsHookEx(ByVal idHook As Integer, ByVal lpfn As HookProc, ByVal hInstance As IntPtr, ByVal threadId As Integer) As Integer
End Function
<DllImport("user32.dll")> _
Private Shared Function SetDlgItemText(ByVal hWnd As IntPtr, ByVal nIDDlgItem As Integer, ByVal lpString As String) As Boolean
End Function
Private Delegate Function HookProc(ByVal nCode As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As Integer
Shared dlgHookProc As HookProc
Private Const WH_CBT As Long = 5
Private Const HCBT_ACTIVATE As Long = 5
Private Const ID_BUT_OK As Integer = 1
Private Const ID_BUT_CANCEL As Integer = 2
Private Const ID_BUT_ABORT As Integer = 3
Private Const ID_BUT_RETRY As Integer = 4
Private Const ID_BUT_IGNORE As Integer = 5
Private Const ID_BUT_YES As Integer = 6
Private Const ID_BUT_NO As Integer = 7
Private Const BUT_OK As String = "Save"
Private Const BUT_CANCEL As String = "Cancelar"
Private Const BUT_ABORT As String = "Stop"
Private Const BUT_RETRY As String = "Continue"
Private Const BUT_IGNORE As String = "Ignore"
Private Const BUT_YES As String = "Si"
Private Const BUT_NO As String = "No"
Private Shared _hook As Integer = 0
Private Shared Function DialogHookProc(ByVal nCode As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As Integer
If nCode < 0 Then
Return CallNextHookEx(_hook, nCode, wParam, lParam)
End If
If nCode = HCBT_ACTIVATE Then
SetDlgItemText(wParam, ID_BUT_OK, BUT_OK)
SetDlgItemText(wParam, ID_BUT_CANCEL, BUT_CANCEL)
SetDlgItemText(wParam, ID_BUT_ABORT, BUT_ABORT)
SetDlgItemText(wParam, ID_BUT_RETRY, BUT_RETRY)
SetDlgItemText(wParam, ID_BUT_IGNORE, BUT_IGNORE)
SetDlgItemText(wParam, ID_BUT_YES, BUT_YES)
SetDlgItemText(wParam, ID_BUT_NO, BUT_NO)
End If
Return CallNextHookEx(_hook, nCode, wParam, lParam)
End Function
Private Sub btn_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btn.Click
dlgHookProc = New HookProc(AddressOf DialogHookProc)
_hook = SetWindowsHookEx(CInt(WH_CBT), dlgHookProc, IntPtr.op_Explicit(0), CInt(GetCurrentThreadId()))
Dim dlgEmptyCheck As DialogResult = MessageBox.Show("Text", "Caption", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button3)
If dlgEmptyCheck = DialogResult.Abort Then
End If
UnhookWindowsHookEx(_hook)
End Sub
Aqui está um snippet C# que usa um gancho Win32 para alterar as legendas do botão (proveniente de http://icodesnip.com/snippet/csharp/custom-messagebox-buttons):
[DllImport("kernel32.dll")]
static extern uint GetCurrentThreadId();
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern int CallNextHookEx(int idHook, int nCode, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern bool UnhookWindowsHookEx(int idHook);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId);
[DllImport("user32.dll")]
private static extern bool SetDlgItemText(IntPtr hWnd, int nIDDlgItem, string lpString);
delegate int HookProc(int nCode, IntPtr wParam, IntPtr lParam);
static HookProc dlgHookProc;
private const long WH_CBT = 5;
private const long HCBT_ACTIVATE = 5;
private const int ID_BUT_OK = 1;
private const int ID_BUT_CANCEL = 2;
private const int ID_BUT_ABORT = 3;
private const int ID_BUT_RETRY = 4;
private const int ID_BUT_IGNORE = 5;
private const int ID_BUT_YES = 6;
private const int ID_BUT_NO = 7;
private const string BUT_OK = "Save";
private const string BUT_CANCEL = "Cancel";
private const string BUT_ABORT = "Stop";
private const string BUT_RETRY = "Continue";
private const string BUT_IGNORE = "Ignore";
private const string BUT_YES = "Yeeh";
private const string BUT_NO = "Never";
private static int _hook = 0;
private static int DialogHookProc(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode < 0)
{
return CallNextHookEx(_hook, nCode, wParam, lParam);
}
if (nCode == HCBT_ACTIVATE)
{
SetDlgItemText(wParam, ID_BUT_OK, BUT_OK);
SetDlgItemText(wParam, ID_BUT_CANCEL, BUT_CANCEL);
SetDlgItemText(wParam, ID_BUT_ABORT, BUT_ABORT);
SetDlgItemText(wParam, ID_BUT_RETRY, BUT_RETRY);
SetDlgItemText(wParam, ID_BUT_IGNORE, BUT_IGNORE);
SetDlgItemText(wParam, ID_BUT_YES, BUT_YES);
SetDlgItemText(wParam, ID_BUT_NO, BUT_NO);
}
return CallNextHookEx(_hook, nCode, wParam, lParam);
}
private void Button_Click(object sender, EventArgs e)
{
dlgHookProc = new HookProc(DialogHookProc);
_hook = SetWindowsHookEx((int)WH_CBT, dlgHookProc, (IntPtr)0, (int)GetCurrentThreadId());
DialogResult dlgEmptyCheck = MessageBox.Show("Text", "Caption", MessageBoxButtons.AbortRetryIgnore, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button3);
if (dlgEmptyCheck == DialogResult.Abort)
{
}
UnhookWindowsHookEx(_hook);
}