Система рендеринга управляет шрифтами вне класса

в течение некоторого времени я пытаюсь найти решение проблемы «чистых шрифтов для каждого приложения».

Я хочу реализовать шрифты 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, которое устанавливает рендеринг шрифта вне класса.

есть ли другие решения этой проблемы?


person dst    schedule 15.03.2011    source источник


Ответы (1)


Я вижу, что это входит в привычку отвечать на свои вопросы :D

ну .. нет проблем, пока я найду ответ,

во всяком случае, решение моего вопроса довольно простое: для отображения кнопки, метки или текста группового поля вне класса (без создания подкласса базового класса и переопределения метода OnPaint) необходимо установить свойство FaltStyle элемента управления в значение system (например, . myControl.FlatStyle = FlatStyle.System;), поэтому система обработает рисунок.

затем установите глобальную оконную процедуру и переопределите сообщение WM_PAINT. вот одно хорошее место, которое я нашел для создания нового логического шрифта и отправки сообщения WM_SETFONT элементу управления.

работает хорошо, но есть более простой способ!

Я также немного поиграл с Font.FromLogFont(LOGFONT), но безуспешно; качество шрифта не установлено, и все же я не знаю причин, почему бы и нет.

если кто-то может объяснить, почему, пожалуйста, сделайте это.

person dst    schedule 19.03.2011