是否有一种简单的方法来显示MessageBox的自定义字体?
对于“简单方法”,我的意思是使用WinAPI或其他技术,但不是从头开始编码整个消息框。
我已经看到很多自定义消息框,但大多数只是不保留默认消息框附加参数的表单,其他自定义消息框只是它们的大小/边界错误所以“ok”按钮被剪切或不正确对齐,并且其他自定义消息框有自己的问题/错误。
我希望如果可能添加一个通用参数来实例这个伟大的自定义消息框设置所需的字体:
原始代码是 @Hans Passant 的C#自定义消息框类,我从很久以前就已经花了很多时间Winforms-How can I make MessageBox appear centered on MainForm?并使用在线翻译器对其进行了翻译:
' [ Centered Messagebox ]
'
' Examples :
'
' Using New MessageBox_Centered(Me)
' MessageBox.Show("Test Text", "Test Title", MessageBoxButtons.OK)
' End Using
#Region " Centered MessageBox Class"
Imports System.Runtime.InteropServices
Imports System.Text
Class MessageBox_Centered
Implements IDisposable
' P/Invoke
Public Class NativeMethods
Delegate Function EnumThreadWndProc(hWnd As IntPtr, lp As IntPtr) As Boolean
<DllImport("user32.dll")> _
Shared Function EnumThreadWindows(tid As Integer, callback As EnumThreadWndProc, lp As IntPtr) As Boolean
End Function
<DllImport("kernel32.dll")> _
Shared Function GetCurrentThreadId() As Integer
End Function
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Unicode)> _
Shared Function GetClassName(hWnd As IntPtr, buffer As StringBuilder, buflen As Integer) As Integer
End Function
<DllImport("user32.dll")> _
Shared Function GetWindowRect(hWnd As IntPtr, ByRef rc As RECT) As Boolean
End Function
<DllImport("user32.dll")> _
Shared Function MoveWindow(hWnd As IntPtr, x As Integer, y As Integer, w As Integer, h As Integer, repaint As Boolean) As Boolean
End Function
Structure RECT
Public Left As Integer
Public Top As Integer
Public Right As Integer
Public Bottom As Integer
End Structure
End Class
Private mTries As Integer = 0
Private mOwner As Form
Public Sub New(owner As Form)
mOwner = owner
owner.BeginInvoke(New MethodInvoker(AddressOf findDialog))
End Sub
Private Sub findDialog()
' Enumerate windows to find the message box
If mTries < 0 Then Return
Dim callback As New NativeMethods.EnumThreadWndProc(AddressOf checkWindow)
If NativeMethods.EnumThreadWindows(NativeMethods.GetCurrentThreadId(), callback, IntPtr.Zero) Then
If System.Threading.Interlocked.Increment(mTries) < 10 Then
mOwner.BeginInvoke(New MethodInvoker(AddressOf findDialog))
End If
End If
End Sub
Private Function checkWindow(hWnd As IntPtr, lp As IntPtr) As Boolean
' Checks if <hWnd> is a dialog
Dim sb As New StringBuilder(260)
NativeMethods.GetClassName(hWnd, sb, sb.Capacity)
If sb.ToString() <> "#32770" Then Return True
' Got it
Dim frmRect As New Rectangle(mOwner.Location, mOwner.Size)
Dim dlgRect As NativeMethods.RECT
NativeMethods.GetWindowRect(hWnd, dlgRect)
NativeMethods.MoveWindow(hWnd, frmRect.Left + (frmRect.Width - dlgRect.Right + dlgRect.Left) \ 2, frmRect.Top + (frmRect.Height - dlgRect.Bottom + dlgRect.Top) \ 2, dlgRect.Right - dlgRect.Left, dlgRect.Bottom - dlgRect.Top, True)
Return False
End Function
Public Sub Dispose() Implements IDisposable.Dispose
mTries = -1
End Sub
End Class
#End Region
更新:
尝试调整@Pete假设的解决方案,我无法做到。
Class MessageBox_Centered : Implements IDisposable
Public Class NativeMethods
Delegate Function EnumThreadWndProc(hWnd As IntPtr, lp As IntPtr) As Boolean
Delegate Function EnumWindowsProc(hWnd As IntPtr, lp As IntPtr) As Boolean
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
Shared Function EnumThreadWindows(tid As Integer, callback As EnumThreadWndProc, lp As IntPtr) As Boolean
End Function
<DllImport("kernel32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
Shared Function GetCurrentThreadId() As Integer
End Function
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
Shared Function GetClassName(hWnd As IntPtr, buffer As StringBuilder, buflen As Integer) As Integer
End Function
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
Shared Function GetWindowRect(hWnd As IntPtr, ByRef rc As RECT) As Boolean
End Function
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
Shared Function MoveWindow(hWnd As IntPtr, x As Integer, y As Integer, w As Integer, h As Integer, repaint As Boolean) As Boolean
End Function
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
Shared Function GetWindowText(ByVal hwnd As IntPtr, ByVal lpString As StringBuilder, ByVal cch As Integer) As Integer
End Function
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
Shared Function EnumChildWindows(hwndParent As IntPtr, lpEnumFunc As EnumWindowsProc, lParam As IntPtr) As <MarshalAs(UnmanagedType.Bool)> Boolean
End Function
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
Shared Function SendMessage(hWnd As IntPtr, Msg As UInt32, wParam As IntPtr, lParam As IntPtr) As IntPtr
End Function
Structure RECT
Public Left As Integer
Public Top As Integer
Public Right As Integer
Public Bottom As Integer
End Structure
End Class
Private mTries As Integer = 0
Private mOwner As Form
Public Sub New(owner As Form)
mOwner = owner
owner.BeginInvoke(New MethodInvoker(AddressOf findDialog))
End Sub
Private Sub findDialog()
' Enumerate windows to find the message box
If mTries < 0 Then Return
Dim callback As New NativeMethods.EnumThreadWndProc(AddressOf checkWindow)
If NativeMethods.EnumThreadWindows(NativeMethods.GetCurrentThreadId(), callback, IntPtr.Zero) Then
If System.Threading.Interlocked.Increment(mTries) < 10 Then
mOwner.BeginInvoke(New MethodInvoker(AddressOf findDialog))
End If
End If
End Sub
Private Function checkWindow(hWnd As IntPtr, lp As IntPtr) As Boolean
' Checks if <hWnd> is a dialog
Dim sb As New StringBuilder(260)
NativeMethods.GetClassName(hWnd, sb, sb.Capacity)
If sb.ToString() <> "#32770" Then
Return True
End If
' Got it
Dim frmRect As New Rectangle(mOwner.Location, mOwner.Size)
Dim dlgRect As NativeMethods.RECT
NativeMethods.GetWindowRect(hWnd, dlgRect)
NativeMethods.MoveWindow(hWnd, frmRect.Left + (frmRect.Width - dlgRect.Right + dlgRect.Left) \ 2, frmRect.Top + (frmRect.Height - dlgRect.Bottom + dlgRect.Top) \ 2, dlgRect.Right - dlgRect.Left, dlgRect.Bottom - dlgRect.Top, True)
' Dim wndText As New StringBuilder()
' NativeMethods.GetWindowText(hWnd2, wndText, 1000)
' SendMessage(hWnd2, WM_SETFONT, f.ToHfont(), new IntPtr(1))
Return False
End Function
Public Sub Dispose() Implements IDisposable.Dispose
mTries = -1
End Sub
End Class
更新2:
这是我需要做的解释。
以@Hans Passant的代码片段为中心的消息框,我需要启动它(实例化)但是使用自定义字体。
一个例子可能是创建一个泛型函数进入中心消息框可能使用类的“新”块来传递所需的字体作为参数然后用该字体做必要的事情来显示消息框居中+使用自定义字体。
所以我需要的是通过增加使用自定义字体的可能性来扩展类。
答案 0 :(得分:3)
我已经回答了这个问题,答案为is here。您只需稍微调整即可,因为您对更改现有字体不感兴趣:
if (hText != IntPtr.Zero) {
// Get the current font
IntPtr hFont = SendMessage(hText, WM_GETFONT, IntPtr.Zero, IntPtr.Zero);
Font font = Font.FromHfont(hFont);
mFont = new Font(new FontFamily("Arial"), font.SizeInPoints, FontStyle.Normal);
SendMessage(hText, WM_SETFONT, mFont.ToHfont(), (IntPtr)1);
}
只有第5行不同。更改所需的字体系列。与此代码相同的基本问题虽然不是那么严重,但您选择的新字体必须符合静态控件的计算大小。对原始字体进行的计算。如果你的新字体是“宽”,那么它将不适合,减少SizeInPoints是唯一的解决方法。
答案 1 :(得分:1)
首先,我为没有在VB中的答案道歉(更新 - 通过C#转换为VB转换器的代码)。当然,你可以很好地阅读C#以理解这一点,我很乐意回答你有关它的任何问题。
就如何查找窗口和静态控件而言,此解决方案不是通用的。您需要根据自己的情况进行调整,但关于如何设置字体的重要部分是可重用的。
线程开头的Thread.Sleep()
有点武断。你可能想等一下(半秒肯定太长),但是消息框需要时间显示,消息框将阻止执行。所以,我解开线程,让它等到消息框明确打开,然后我开始寻找它。
另外,请务必最终在HFont上致电DeleteObject()
。
Public Partial Class Form1
Inherits Form
Private Const WM_SETFONT As UInt32 = &H30
Private Delegate Function EnumThreadDelegate(hwnd As IntPtr, lParam As IntPtr) As Boolean
Private Delegate Function EnumWindowsProc(hWnd As IntPtr, lParam As IntPtr) As Boolean
<DllImport("user32.dll")> _
Private Shared Function EnumThreadWindows(dwThreadId As UInteger, lpfn As EnumThreadDelegate, lParam As IntPtr) As <MarshalAs(UnmanagedType.Bool)> Boolean
End Function
<DllImport("kernel32.dll")> _
Private Shared Function GetCurrentThreadId() As UInteger
End Function
<DllImport("user32.dll", SetLastError := True, CharSet := CharSet.Auto)> _
Private Shared Function GetClassName(hWnd As IntPtr, lpClassName As StringBuilder, nMaxCount As Integer) As Integer
End Function
<DllImport("user32.dll")> _
Private Shared Function EnumChildWindows(hwndParent As IntPtr, lpEnumFunc As EnumWindowsProc, lParam As IntPtr) As <MarshalAs(UnmanagedType.Bool)> Boolean
End Function
<DllImport("user32.dll", CharSet := CharSet.Auto, SetLastError := True)> _
Private Shared Function GetWindowText(hWnd As IntPtr, lpString As StringBuilder, nMaxCount As Integer) As Integer
End Function
<DllImport("user32.dll", CharSet := CharSet.Auto)> _
Private Shared Function SendMessage(hWnd As IntPtr, Msg As UInt32, wParam As IntPtr, lParam As IntPtr) As IntPtr
End Function
Shared threadId As UInteger = GetCurrentThreadId()
Public Sub New()
InitializeComponent()
End Sub
Private Sub button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim t As New Thread(New ThreadStart(AddressOf FixMsgBoxFont))
t.Start()
MessageBox.Show(Me, "MyMsg", "Test")
End Sub
Private Sub FixMsgBoxFont()
Thread.Sleep(500)
EnumThreadWindows(threadId, New EnumThreadDelegate(Function(hWnd, lParam)
Dim className As New StringBuilder()
GetClassName(hWnd, className, 1000)
' Look for the message box window
If className.ToString() <> "#32770" Then
Return True
End If
EnumChildWindows(hWnd, New EnumWindowsProc(Function(hWnd2, lParam2)
Dim wndText As New StringBuilder()
GetWindowText(hWnd2, wndText, 1000)
' Look for the static control with our text
If wndText.ToString() = "MyMsg" Then
' Replace the font being used with 8pt Comix Sans MS
Dim f As New Font(New FontFamily("Comic Sans MS"), 8, FontStyle.Bold, GraphicsUnit.Pixel)
' In real life, you'll eventually want to eventually call
' the Windows API DeleteObject() on the font handle
' below or it will leak.
Dim fontHandle As IntPtr = f.ToHfont()
SendMessage(hWnd2, WM_SETFONT, f.ToHfont(), New IntPtr(1))
Return False
End If
Return True
End Function), IntPtr.Zero)
Return False
End Function), IntPtr.Zero)
End Sub
End Class
答案 2 :(得分:0)
这是代码!
它有我需要解决的字体大小问题,但现在已经解决了!
' The author of this code is Hand Passant:
' http://stackoverflow.com/questions/2259027/bold-text-in-messagebox/2259213#2259213
'
' I've just translated it to VB.NET and made very little modifications.
Imports System.Drawing
Imports System.Runtime.InteropServices
Imports System.Text
Imports System.Windows.Forms
Class CustomMessageBox : Implements IDisposable
Private mTries As Integer = 0
Private mOwner As Form
Private mFont As Font
' P/Invoke declarations
Private Const WM_SETFONT As Integer = &H30
Private Const WM_GETFONT As Integer = &H31
Private Delegate Function EnumThreadWndProc(hWnd As IntPtr, lp As IntPtr) As Boolean
<DllImport("user32.dll")> _
Private Shared Function EnumThreadWindows(tid As Integer, callback As EnumThreadWndProc, lp As IntPtr) As Boolean
End Function
<DllImport("kernel32.dll")> _
Private Shared Function GetCurrentThreadId() As Integer
End Function
<DllImport("user32.dll")> _
Private Shared Function GetClassName(hWnd As IntPtr, buffer As StringBuilder, buflen As Integer) As Integer
End Function
<DllImport("user32.dll")> _
Private Shared Function GetDlgItem(hWnd As IntPtr, item As Integer) As IntPtr
End Function
<DllImport("user32.dll")> _
Private Shared Function SendMessage(hWnd As IntPtr, msg As Integer, wp As IntPtr, lp As IntPtr) As IntPtr
End Function
<DllImport("user32.dll")> _
Shared Function GetWindowRect(hWnd As IntPtr, ByRef rc As RECT) As Boolean
End Function
<DllImport("user32.dll")> _
Shared Function MoveWindow(hWnd As IntPtr, x As Integer, y As Integer, w As Integer, h As Integer, repaint As Boolean) As Boolean
End Function
Structure RECT
Public Left As Integer
Public Top As Integer
Public Right As Integer
Public Bottom As Integer
End Structure
Public Sub New(owner As Form, Optional Custom_Font As Font = Nothing)
mOwner = owner
mFont = Custom_Font
owner.BeginInvoke(New MethodInvoker(AddressOf findDialog))
End Sub
Private Sub findDialog()
' Enumerate windows to find the message box
If mTries < 0 Then
Return
End If
Dim callback As New EnumThreadWndProc(AddressOf checkWindow)
If EnumThreadWindows(GetCurrentThreadId(), callback, IntPtr.Zero) Then
If System.Threading.Interlocked.Increment(mTries) < 10 Then
mOwner.BeginInvoke(New MethodInvoker(AddressOf findDialog))
End If
End If
End Sub
Private Function checkWindow(hWnd As IntPtr, lp As IntPtr) As Boolean
' Checks if <hWnd> is a dialog
Dim sb As New StringBuilder(260)
GetClassName(hWnd, sb, sb.Capacity)
If sb.ToString() <> "#32770" Then Return True
' Got it, get the STATIC control that displays the text
Dim hText As IntPtr = GetDlgItem(hWnd, &HFFFF)
Dim frmRect As New Rectangle(mOwner.Location, mOwner.Size)
Dim dlgRect As RECT
GetWindowRect(hWnd, dlgRect)
MoveWindow(hWnd, frmRect.Left + (frmRect.Width - dlgRect.Right + dlgRect.Left) \ 2, frmRect.Top + (frmRect.Height - dlgRect.Bottom + dlgRect.Top) \ 2, dlgRect.Right - dlgRect.Left, dlgRect.Bottom - dlgRect.Top, True)
If hText <> IntPtr.Zero Then
If mFont Is Nothing Then
' Get the current font
mFont = Font.FromHfont(SendMessage(hText, WM_GETFONT, IntPtr.Zero, IntPtr.Zero))
End If
SendMessage(hText, WM_SETFONT, mFont.ToHfont(), New IntPtr(1))
End If
' Done
Return False
End Function
Public Sub Dispose() Implements IDisposable.Dispose
mTries = -1
mOwner = Nothing
If mFont IsNot Nothing Then mFont.Dispose()
End Sub
End Class
用法:
Using New CustomMessageBox(Me, New Font(New FontFamily("Lucida Console"), Font.SizeInPoints, FontStyle.Bold))
MessageBox.Show("Test Text", "Test Title", MessageBoxButtons.OK)
End Using