渲染系统控制类外的字体

时间:2011-03-15 09:33:00

标签: c# winforms winapi fonts cleartype

很长一段时间以来,我正试图找到“per-apllication cleartype fonts”问题的解决方案。

我想为TextBoxButtonComboBox或任何其他非自定义/库存控件实施cleartype字体。

好吧,有一种简单的方法可以实现我所知道的,即覆盖OnPaint方法:

protected override void OnPaint(PaintEventArgs pevent)
{
    pevent.Graphics.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;

    base.OnPaint(pevent);
}

这很好用,但问题是我想这样做而不继承任何这些控件,并以某种方式改变类外的字体属性。

到目前为止,我使用的方法没有取得太大成功,是安装一个'Hook Procedure'并监听发送给控件的消息。下面的代码是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属性。

这个问题还有其他解决办法吗?

1 个答案:

答案 0 :(得分:0)

我看到它开始成为回答我自己问题的习惯:D

好吧..只要我找到答案就没问题,

无论如何,我的问题的解决方案很简单: 要在类外部呈现按钮,标签或组框文本(不对基类进行子类化并重写OnPaint方法),应将控件的FaltStyle属性设置为system(例如myControl.FlatStyle = FlatStyle.System;) ,所以系统将处理绘图。

然后,安装一个全局窗口过程并覆盖WM_PAINT消息。这是我发现创建新逻辑字体并将WM_SETFONT消息发送到控件的一个好地方。

效果很好,但是,有一种更简单的方法!

我也曾在Font.FromLogFont(LOGFONT)玩过一段时间,但没有取得任何成功; 它没有设置的字体质量,但我不知道为什么不这样做。

如果有人能解释原因,请这样做。