уже довольно давно я пытаюсь найти решение проблемы "per-apllication cleartype fonts".
Я хочу реализовать шрифты cleartype для TextBox
, или Button
, или ComboBox
, или любого другого нестандартного / стандартного элемента управления.
Ну, есть один простой способ добиться этого, о котором я знаю, и это переопределение метода OnPaint:
protected override void OnPaint(PaintEventArgs pevent)
{
pevent.Graphics.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;
base.OnPaint(pevent);
}
и это прекрасно работает, но дело в том, что я хочу сделать это без наследования какого-либо из этих элементов управления и каким-то образом изменить свойства шрифта вне класса.
метод, который я использовал до сих пор без особого успеха, состоял в том, чтобы установить «процедуру подключения» и прослушать сообщения, отправленные элементам управления. код ниже является частью Form1 class
:
// the delegate with the same signature as the callback function
private delegate int HookMessages(int nCode, IntPtr wParam, IntPtr lParam);
// when the Load event of the Form takes place the controls are created
// and the method that sets the hooks is called
private void Form1_Load(object sender, EventArgs e)
{
Button btn = new Button();
btn.Name = "btn";
btn.Parent = this;
btn.Size = new Size(this.ClientRectangle.Width - 10, 20);
btn.Location = new Point(5, tbx.Top + tbx.Height + 10);
btn.BackColor = Color.FromArgb(100, 1, 1, 1);
btn.Text = "Click ME";
HookControlMessages();
}
// the hook that monitors control's events is set for the current thread
private void HookControlMessages()
{
if (hookWndHandle == 0)
{
HookWndProcedure = new HookMessages(HookWndProcMessages);
hookWndHandle = SetWindowsHookEx(
WindowHookTypes.WH_CALLWNDPROC,
HookWndProcedure,
IntPtr.Zero,
(int)GetCurrentThreadId());
if (hookWndHandle == 0)
{
MessageBox.Show("Window`s messages hooking failed.");
return;
}
}
}
// the callback function
private static int HookWndProcMessages(int nCode, IntPtr wParam, IntPtr lParam)
{
int nextHook = 0;
try
{
if (nCode >= 0)
{
CWPSTRUCT m = (CWPSTRUCT)Marshal.PtrToStructure(lParam, typeof(CWPSTRUCT));
_thisInstance.CheckWndProcMsgs(m);
}
nextHook = CallNextHookEx(hookWndHandle, nCode, wParam, lParam);
}
catch
{
nextHook = 0;
}
return nextHook;
}
private void CheckWndProcMsgs(CWPSTRUCT m)
{
if (m.hwnd == ((this.Controls.Find("btn", true) as Control[])[0] as Button).Handle)
{
#region BUTTON
switch (m.message)
{
case (int)WindowsMessages.WM_CREATE:
{
// when the handle of the control is created also a new font is created
// and a WM_SETFONT message sent along with the specification that a redraw must be performed
Rect rc = new Rect(new Rectangle(0, 0, ((this.Controls.Find("btn", true) as Control[])[0] as UButton).Width, ((this.Controls.Find("btn", true) as Control[])[0] as UButton).Height));
hFontNew = CreateFont(
25, 5, 2, 0, 900, 0, 0, 0,
(byte)FontCharSet.OEM_CHARSET,
(byte)FontPrecision.OUT_DEFAULT_PRECIS,
(byte)FontClipPrecision.CLIP_DEFAULT_PRECIS,
(byte)FontQuality.DEFAULT_QUALITY,
(byte)(FontPitchAndFamily.DEFAULT_PITCH | FontPitchAndFamily.FF_DONTCARE),
"Verdana");
SendMessage(m.hwnd, (uint)WindowsMessages.WM_SETFONT, hFontNew, (IntPtr)1);
}
break;
case (int)WindowsMessages.WM_SETFONT:
{
// the purpose of this block is to verify if the new font is set correct
LOGFONT lf = new LOGFONT();
IntPtr lf0 = Marshal.AllocCoTaskMem(Marshal.SizeOf(lf));
GetObject((IntPtr)m.wparam, Marshal.SizeOf(lf), lf0);
lf = (LOGFONT)Marshal.PtrToStructure(lf0, lf.GetType());
Marshal.FreeCoTaskMem(lf0);
}
break;
case (int)WindowsMessages.WM_DESTROY:
{
// when the control is destroyed the new font is also deleted
if (hFontNew != IntPtr.Zero)
{
DeleteObject(hFontNew);
}
}
break;
}
#endregion
}
}
эта часть будет отлично работать для окна, созданного с помощью функций CreateWindow / CreateWindowEx, или если Paint event
будет обрабатываться пользователем для пользовательской краски, но я хочу оставить всю картину в системе и изменять только шрифт без каких-либо других вмешательств в элемент управления.
должен ли этот подход работать? если нет, может кто-нибудь объяснить мне, почему? Я знаю, что когда система рисует элементы управления, она использует шрифт, заданный свойством Control.Font
класса, но не должен ли он также использовать рендеринг шрифта из оконных сообщений?
когда класс обрабатывает системные сообщения в методе WndProc
, разве он не ретранслирует данные, поступившие в Message.WParam & Message.LParam
из WndProc
во время рисования?
например. при рисовании текста не должна ли система использовать WM_GETFONT, чтобы использовать соответствующий шрифт?
Я, например, не знаю ни одного свойства Control, которое устанавливает рендеринг шрифта вне класса.
есть ли другие способы решения этой проблемы?