简体   繁体   中英

How to use IVsFontAndColorEvents?

Microsoft's own site does not explain in details how to use this interface. They claim that this is the way to get notified if the Fonts & Colors change in Visual Studio.

I tried what seemed to be an obvious choice and implemented the interface on my package, but there were no attributes mentioned I should set on my VSPackage. Unfortunately that doesn't seem to be enough.

Here's a sample of what I did:

public class SceVSIPackage : Package, IVsFontAndColorEvents
{
    public int OnApply()
    {
        return VSConstants.S_OK;
    }

    public int OnFontChanged(ref Guid rguidCategory, FontInfo[] pInfo, LOGFONTW[] pLOGFONT, uint HFONT)
    {
        return VSConstants.S_OK;
    }

    public int OnItemChanged(ref Guid rguidCategory, string szItem, int iItem, ColorableItemInfo[] pInfo, uint crLiteralForeground, uint crLiteralBackground)
    {
        return VSConstants.S_OK;
    }

    public int OnReset(ref Guid rguidCategory)
    {
        return VSConstants.S_OK;
    }

    public int OnResetToBaseCategory(ref Guid rguidCategory)
    {
        return VSConstants.S_OK;
    }
}

Unfortunately none of the IVsFontAndColorEvent members (all the methods above) get called.

Do I miss something else? Like an attribute? Or proffering the service?

I also tried serviceContainer.AddService(typeof(IVsFontAndColorEvent), this, true); but it didn't help either.

Workaround

Unfortunately I couldn't make IVsFontAndColorEvents working. However, I could achieve the same (getting notified when the Fonts change in Tools\\Options\\Fonts and Colors\\Text Editor ) with the code found here .

The idea is to use TextManagerEvents instead of IVsFontAndColorEvents :

//using Microsoft.VisualStudio.TextManager.Interop;
IVsTextManager textManager = GetService(typeof(SVsTextManager)) as IVsTextManager;
if (textManager != null)
{
    IConnectionPointContainer container = textManager as IConnectionPointContainer;
    if (container != null)
    {
        IConnectionPoint textManagerEventsConnection;
        Guid eventGuid = typeof(IVsTextManagerEvents).GUID;
        container.FindConnectionPoint(ref eventGuid, out textManagerEventsConnection);
        if (textManagerEventsConnection != null)
        {
            TextManagerEvents textManagerEvents = new TextManagerEvents();
            uint textManagerCookie;
            textManagerEventsConnection.Advise(textManagerEvents, out textManagerCookie);
            if (textManagerCookie != 0)
            {
                textManagerEvents.FontColorPreferencesChanged += OnFontColorPreferencesChanged;
            }
        }
    }
}

Notes

1. OnFontColorPreferencesChanged

Just in case you are also interested in how to extract the font and color information, here is how I did it:

private FontInfo prevFontInfo;  // Store previous FontInfo to prevent execution of the event handler multiple times.

private void OnFontColorPreferencesChanged(object sender, EventArgs e)
{
    IVsFontAndColorStorage fontAndColorStorage = GetService(typeof(SVsFontAndColorStorage)) as IVsFontAndColorStorage;
    if (fontAndColorStorage != null)
    {
        // GlobalValues.FontsAndColors_TextEditor is found in the registry: HKEY_USERS\.DEFAULT\Software\Microsoft\VisualStudio\[VS_VER]_Config\FontAndColo‌​rs\Text Editor, where VS_VER is the actual Visual Studio version: 10.0, 11.0, 12.0, 14.0, etc.
        if (fontAndColorStorage.OpenCategory(GlobalValues.FontsAndColors_TextEditor, (uint)__FCSTORAGEFLAGS.FCSF_LOADDEFAULTS) == VSConstants.S_OK)
        {
            LOGFONTW[] logFontw = new LOGFONTW[1];  // Only 1 item expected
            FontInfo[] fontInfo = new FontInfo[1];  // Only 1 item expected
            if (fontAndColorStorage.GetFont(logFontw, fontInfo) == VSConstants.S_OK &&
                !prevFontInfo.Equals(fontInfo[0]))
            {
                prevFontInfo = fontInfo[0];

                // FontInfo uses pixels as units, WPF uses points. Conversion between the two is required.
                double fontSize = (double)new FontSizeConverter().ConvertFrom(string.Format("{0}pt", fontInfo.wPointSize));    
                FontFamily fontFamily = new FontFamily(fontInfo.bstrFaceName);
                // There you go, you have the FontFamily and size ready to use.
            }
            fontAndColorStorage.CloseCategory();
        }
    }
}

2. Limitations

Although this solutions is a usable workaround for me, it has some problems:

  • when changing the font of the Text Editor, the OnFontColorPreferencesChanged event is raised multiple times. I can't tell if IVsFontAndColorEvents would raise the event only once or had the same problem (as I never got it working.) I solved this issue by introducing prevFontInfo and don't invoke my logic unless this value is different from fontInfo[0], the values I just read.
  • the event fires only when the Text Editor fonts and colors are changed, but not when any of the rest (eg Environment Font or Output Window )
  • the event does not fire when the bold option is changed. Nevertheless, the font weight is not seemed to be used by the IDE anyway...
  • the event does not fire when "Use Defaults" is selected in Options/Fonts and Colors . As a matter of fact it doesn't fire either when it's reset to the default values by manually entering them (eg: font size to 10)

I hope some of these might be useful for someone stumbling upon this question.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM