问题
OK I'm working on a problem tyring to embed a LCD type true type font in a text box. As for some background, I can get the lcd font to display if I install the font onto my system, then load it as the font type for the text box and it works great. However, it will not work as an embedded font in the application. I"m using a a Windows Forms Application in Visual Basic, from the Microsoft Visual Studio 2010 on a Windows 7 box.
I have tried the following code using a private font collection from memory after storing the font as a resource file and setting the property to embed resource.
Imports System.Drawing.Text
Imports System.Runtime.InteropServices
Module CustomFont
'PRIVATE FONT COLLECTION TO HOLD THE DYNAMIC FONT
Private _pfc As PrivateFontCollection = Nothing
Public ReadOnly Property GetInstance(ByVal Size As Single, _
ByVal style As FontStyle) As Font
Get
'IF THIS IS THE FIRST TIME GETTING AN INSTANCE
'LOAD THE FONT FROM RESOURCES
If _pfc Is Nothing Then LoadFont()
'RETURN A NEW FONT OBJECT BASED ON THE SIZE AND STYLE PASSED IN
Return New Font(_pfc.Families(0), Size, style)
End Get
End Property
Private Sub LoadFont()
Try
'INIT THE FONT COLLECTION
_pfc = New PrivateFontCollection
'LOAD MEMORY POINTER FOR FONT RESOURCE
Dim fontMemPointer As IntPtr = _
Marshal.AllocCoTaskMem( _
My.Resources.DIGITALDREAMNARROW.Length)
'COPY THE DATA TO THE MEMORY LOCATION
Marshal.Copy(My.Resources.DIGITALDREAMNARROW, _
0, fontMemPointer, _
My.Resources.DIGITALDREAMNARROW.Length)
'LOAD THE MEMORY FONT INTO THE PRIVATE FONT COLLECTION
_pfc.AddMemoryFont(fontMemPointer, _
My.Resources.DIGITALDREAMNARROW.Length)
'FREE UNSAFE MEMORY
Marshal.FreeCoTaskMem(fontMemPointer)
Catch ex As Exception
'ERROR LOADING FONT. HANDLE EXCEPTION HERE
End Try
End Sub
End Module
The problem with this code is that you are supposed to enable the control's UseCompatibleTextRendering property to true. Granted if using this module on a lable or button text it works great. For a text box however, there is no UseCompatibleTextRendering property.
I've come to understand that the text boxes use GDI rendering whereas the other text controls use GDI+ ( I may have those switched around, so don't quote me on that, All i remember is that they are different).
I found some older code snipits trying that use the AddFontMemResourceEX function from the gdi32.dll file in windows, and some have claimed that it works for use in text boxes. So I created the following class.
Imports System
Imports System.Drawing.Text
Imports System.IO
Imports System.Reflection
Public Class GetLCDFont
Private Declare Auto Function AddFontMemResourceEX Lib "gdi32.dll" _
(ByVal pbFont As Integer, ByVal cbFont As Integer, _
ByVal pdv As Integer, ByRef pcFonts As Integer) As IntPtr
Public Shared Function GetFont(ByVal fontName As String) As FontFamily
Dim exeCurrent As [Assembly] = [Assembly].GetExecutingAssembly()
Dim nameSpc As String = exeCurrent.GetName().Name.ToString()
Dim fontCollection As New PrivateFontCollection
Dim loadStream As Stream = exeCurrent.GetManifestResourceStream( _
nameSpc + "." + fontName)
Dim byteBuffer(CType(loadStream.Length, Integer)) As Byte
loadStream.Read(byteBuffer, 0, Int(CType(loadStream.Length, Integer)))
Dim fontPtr As IntPtr = Runtime.InteropServices.Marshal.AllocHGlobal( _
Runtime.InteropServices.Marshal.SizeOf(GetType(Byte)) * _
byteBuffer.Length)
Runtime.InteropServices.Marshal.Copy(byteBuffer, 0, fontPtr, byteBuffer.Length)
fontCollection.AddMemoryFont(fontPtr, byteBuffer.Length)
Dim pcFonts As Int32 = 1
AddFontMemResourceEX(fontPtr, byteBuffer.Length, 0, pcFonts)
Runtime.InteropServices.Marshal.FreeHGlobal(fontPtr)
Return fontCollection.Families(0)
End Function
Public Sub New()
End Sub
Protected Overrides Sub Finalize()
MyBase.Finalize()
End Sub
End Class
However when calling this class, I get a InvalidOperatioException was unhandled. The error is Unable to find an entry pointed named 'AddFontMemResourceEX in DLL 'gdi32.dll'.
Hoping someone could help me either tell me what i'm going wrong, or point me in a direction that would help me embed a font for use in text boxes for use with a Windows Forms Application.
Most of the examples referenced at MSDN all point to how to package fonts when using a WPF application.
Thank you.
回答1:
I've been using the above code for labels, but never tried with a text box.
You could create a custom text box class that inherits from Textbox and then override the WndProc method, since the OnPaint won't render text.
Public Class CustomTextBox
Inherits TextBox
Public Const WM_NCPAINT As Integer = &H85
<DllImport("User32.dll")> _
Public Shared Function GetWindowDC(ByVal hWnd As IntPtr) As IntPtr
End Function
<DllImport("user32.dll")> _
Private Shared Function ReleaseDC(ByVal hWnd As IntPtr, ByVal hDC As IntPtr) As Boolean
End Function
Protected Overrides Sub WndProc(ByRef m As Message)
MyBase.WndProc(m)
If m.Msg = WM_NCPAINT Then
Dim hDC As IntPtr = GetWindowDC(m.HWnd)
Using g As Graphics = Graphics.FromHdc(hDC)
g.DrawString(Me.Text, GetInstance(10, FontStyle.Bold), New SolidBrush(Me.ForeColor), Me.ClientRectangle)
End Using
ReleaseDC(m.HWnd, hDC)
End If
End Sub
End Class
回答2:
Although it's not as clean, you can use your installer to put the fonts in your application directory, and load them into your PrivateFontCollection using:
For Each fontfile As String In System.IO.Directory.GetFiles(filepath & "\Fonts", "*.ttf")
_pfc.AddFontFile(fontfile)
Next fontfile
I'm not sure why Microsoft treats this differently, but my TextBoxes and ComboBoxes now use my custom fonts even though they don't have a UseCompatibleTextRendering property.
来源:https://stackoverflow.com/questions/15042199/embed-custom-font-fot-text-box-use-in-visual-basic-2010