3

I am using HelpProvider to show help for my control.

I input the help string for HelpProvider control. But this does not show the string properly.

P/S:

My language is Vietnam, which is a unicode font.

Here is my text when progamming: "Chúc mừng năm mới"

Here is the text when shown:

enter image description here

xanatos
  • 109,618
  • 12
  • 197
  • 280
user3819222
  • 127
  • 8
  • Are you sure that font has the characters? It would be same font as the control with the helpstring – Charlieface Jan 01 '21 at 02:21
  • It was a known bug (https://social.msdn.microsoft.com/Forums/en-US/5e057bf4-c1d2-4099-80e4-8c72ab4cfc35/helpprovider-and-unicode?forum=winforms)... Sadly the HelpProvider is one of the unknown features that no one ever used and so the bugs it had remained – xanatos Jan 01 '21 at 09:53
  • See if you can use `ToolTip`: https://stackoverflow.com/a/28260468/613130 – xanatos Jan 01 '21 at 09:53
  • @Charlieface No, it doesn't. It uses a fixed font (tried setting a control with Comic Sans, the help tip was still in the original font) – xanatos Jan 01 '21 at 11:10
  • @xanatos The problem is in [source code of Help class](https://referencesource.microsoft.com/?WT.mc_id=DT-MVP-5003235#System.Windows.Forms/winforms/Managed/System/WinForms/Help.cs,160) which has created the `NativeMethods.HH_POPUP` but didn't specified any font, so a default font which doesn't support Unicode characters will be used. The fix could be using a default font like `SystemFonts.CaptionFont` which supports Unicode characters. – Reza Aghaei Jan 01 '21 at 19:49
  • 1
    I posted an answer here to show how to fix it, also created a [HelpProvider2 Component](https://github.com/r-aghaei/HelpProvider2Example) which supports Unicode, Font, ForeColor and BackColor. I may suggest a Fix for .NET 5. – Reza Aghaei Jan 01 '21 at 19:49
  • @RezaAghaei Aaaah interesting... Didn't know you could set the font... – xanatos Jan 01 '21 at 19:58
  • @xanatos The built-in HelpProvider doesn't support Font, but the underlying API uses [`HH_POPUP`](https://learn.microsoft.com/en-us/windows/win32/api/htmlhelp/ns-htmlhelp-hh_popup?WT.mc_id=DT-MVP-5003235) which supports Font as well as ForeColor and BackColor. – Reza Aghaei Jan 01 '21 at 20:09

1 Answers1

3

It's an old bug and it's because of two problems:

  • The default font which is used by underlying API of the HelpProvider doesn't support unicode characters
  • The underlying API of the HelpProvider doesn't support Unicode.

After fixing these two problems, you can show Unicode characters correctly:

HelpExtensions.ShowPopup2(button1, "متن آزمایشی", Control.MousePosition);

enter image description here

The first problem is in Help class(.NET 4.X, .NET 5) which has created the HH_POPUP but hasn't specified any font for it. As a result a default font which doesn't support Unicode characters will be used.

  • A possible fix is using a default font like SystemFonts.CaptionFont which supports Unicode characters.

For the second problem, you need to change a setting in Windows, to do so:

  • Go to Control Panel → Region → Administrative tab, then in the section "Language for non-Unicode programs", click on "Change system locale ..." button and then in the next dialog, choose the language of your choice, for example Persian.

    Or to support other languages as well you can choose: "Beta: Use Unicode UTF-8 for worldwide language support" which is Beta.

enter image description here

And here is HelpExtensions class:

using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;
public static class HelpExtensions
{
    public static void ShowPopup2(Control parent, string caption, Point location, Font font = null, Color? backColor = null, Color? foreColor = null)
    {
        font = font ?? SystemFonts.CaptionFont;
        backColor = backColor ?? Color.FromKnownColor(KnownColor.Window);
        foreColor = foreColor ?? Color.FromKnownColor(KnownColor.WindowText);

        var popup = new HH_POPUP();
        popup.clrBackground = new COLORREF(backColor.Value);
        popup.clrForeground = new COLORREF(foreColor.Value);
        popup.pt = new POINT(location);
        var pszText = Marshal.StringToCoTaskMemAuto(caption);
        popup.pszText = pszText;
        var pszFont = Marshal.StringToCoTaskMemAuto(
            $"{font.Name}, {font.Size}, , " +
            $"{(font.Bold ? "BOLD" : "")}" +
            $"{(font.Italic ? "ITALIC" : "")}" +
            $"{(font.Underline ? "UNDERLINE" : "")}");
        popup.pszFont = pszFont;
        try
        {
            HtmlHelp(parent.Handle, null, HTMLHelpCommand.HH_DISPLAY_TEXT_POPUP, popup);
        }
        finally
        {
            Marshal.FreeCoTaskMem(pszText);
            Marshal.FreeCoTaskMem(pszFont);
        }
    }

    [Flags()]
    public enum HTMLHelpCommand : uint
    {
        HH_DISPLAY_TOPIC = 0,
        HH_DISPLAY_TOC = 1,
        HH_DISPLAY_INDEX = 2,
        HH_DISPLAY_SEARCH = 3,
        HH_DISPLAY_TEXT_POPUP = 0x000E,
        HH_HELP_CONTEXT = 0x000F,
        HH_CLOSE_ALL = 0x0012
    }

    [DllImport("hhctrl.ocx", SetLastError = true, EntryPoint = "HtmlHelpW", CharSet = CharSet.Unicode)]
    static extern int HtmlHelp(IntPtr hWndCaller,
        [MarshalAs(UnmanagedType.LPWStr)] string pszFile,
        HTMLHelpCommand uCommand,
        [MarshalAs(UnmanagedType.LPStruct)] HH_POPUP dwData);

    [StructLayout(LayoutKind.Sequential)]
    struct COLORREF
    {
        int ColorRef;

        public COLORREF(int lRGB)
        {
            ColorRef = lRGB & 0x00ffffff;
        }
        public COLORREF(Color color) : this(color.ToArgb())
        {
        }
    }
    [StructLayout(LayoutKind.Sequential)]
    class POINT
    {
        public int x;
        public int y;
        public POINT(int x, int y)
        {
            this.x = x;
            this.y = y;
        }
        public POINT(Point p) : this(p.X, p.Y)
        {
        }
    }
    [StructLayout(LayoutKind.Sequential)]
    struct RECT
    {
        public int left;
        public int top;
        public int right;
        public int bottom;
        public RECT(int left, int top, int right, int bottom)
        {
            this.left = left;
            this.top = top;
            this.right = right;
            this.bottom = bottom;
        }
        public RECT(Rectangle r) : this(r.Left, r.Top, r.Right, r.Bottom)
        {
        }
    }
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    class HH_POPUP
    {
        internal int cbStruct = Marshal.SizeOf(typeof(HH_POPUP));
        internal IntPtr hinst = IntPtr.Zero;
        internal int idString = 0;
        internal IntPtr pszText;
        internal POINT pt;
        internal COLORREF clrForeground = new COLORREF(-1);
        internal COLORREF clrBackground = new COLORREF(-1);
        internal RECT rcMargins = new RECT(-1, -1, -1, -1);
        internal IntPtr pszFont;
    }
}

HelpProvider2 Component

I've created a HelpProvider2 component which supports Unicode characters. It also exposes Font, ForeColor and BackColor properties:

HelpProvider2Properties.png

Download or clone

Reza Aghaei
  • 120,393
  • 18
  • 203
  • 398