Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sample request: Hosting IWpfTextView in Dialog Window #286

Open
brukwa opened this issue Sep 9, 2023 · 1 comment
Open

Sample request: Hosting IWpfTextView in Dialog Window #286

brukwa opened this issue Sep 9, 2023 · 1 comment

Comments

@brukwa
Copy link

brukwa commented Sep 9, 2023

Installed products

  • Visual Studio: [example Professional 2022]

Description

How to embed Visual Studio editor (IWpfTextView) within custom dialog box?

I have this code, that almost work... but the editor is read-only. No mater what I do it does not process any input (keyboard nor mouse).

public partial class WndHostEditor : DialogWindow
{
    public WndHostEditor(AsyncPackage package)
        : base()
    {
        InitializeComponent();

        IComponentModel componentModel = m_package.GetService<SComponentModel, IComponentModel>();
        IContentTypeRegistryService svcContentTypeRegistry = componentModel.GetService<IContentTypeRegistryService>();
        ITextBufferFactoryService svcTextBufferFactory = componentModel.GetService<ITextBufferFactoryService>();
        ITextEditorFactoryService svcTextEditorFactory = componentModel.GetService<ITextEditorFactoryService>();
        IProjectionBufferFactoryService svcProjectionBufferFactory = componentModel.GetService<IProjectionBufferFactoryService>();

        var contentType = svcContentTypeRegistry.GetContentType("json");
        var buffer = svcTextBufferFactory.CreateTextBuffer("{\"Key\":\"Value\"}", contentType);
        var view = svcTextEditorFactory.CreateTextView(buffer);

        this.CtrlEditor.Content = svcTextEditorFactory.CreateTextViewHost(view, true).HostControl;
    } 
}

There is an extension that hosts mentioned editor on custom dialog box, but there is no code (beside decompiling it).
https://marketplace.visualstudio.com/items?itemName=VisualStudioProductTeam.RegexEditor

Someone has posted similar question on stackoverflow without any response 😭
https://stackoverflow.com/questions/70091440/editor-pane-in-iwpftextview-is-not-editable

@tonyhallett
Copy link

Certainly appears doable with a ToolWindowPane see https://joshvarty.com/2014/08/01/ripping-the-visual-studio-editor-apart-with-projection-buffers/

I can get the host control using the services, it is not read only but certain keys such as backspace does not work.

    public static class HostEditor {
        private static Microsoft.VisualStudio.OLE.Interop.IServiceProvider cachedOleServiceProvider;
        private static  Microsoft.VisualStudio.OLE.Interop.IServiceProvider OleServiceProvider
        {
            get
            {
                ThreadHelper.ThrowIfNotOnUIThread();
                if (cachedOleServiceProvider == null)
                {
                    IObjectWithSite objWithSite = ServiceProvider.GlobalProvider;
                    Guid interfaceIID = typeof(Microsoft.VisualStudio.OLE.Interop.IServiceProvider).GUID;
                    IntPtr rawSP;
                    objWithSite.GetSite(ref interfaceIID, out rawSP);
                    try
                    {
                        if (rawSP != IntPtr.Zero)
                        {
                            cachedOleServiceProvider = (Microsoft.VisualStudio.OLE.Interop.IServiceProvider)Marshal.GetObjectForIUnknown(rawSP);
                        }
                    }
                    finally
                    {
                        if (rawSP != IntPtr.Zero)
                        {
                            Marshal.Release(rawSP);
                        }
                    }
                }

                return cachedOleServiceProvider;
            }
        }
        private static Services VsServices { get; } = new Services();
        private class Services
        {
            public Services()
            {
                ContentTypeRegistryService = VS.GetMefService<IContentTypeRegistryService>();
                EditorOptionsFactoryService = VS.GetMefService<IEditorOptionsFactoryService>();
                VsEditorAdaptersFactoryService = VS.GetMefService<IVsEditorAdaptersFactoryService>();
            }
            public IContentTypeRegistryService ContentTypeRegistryService { get; }
            public IEditorOptionsFactoryService EditorOptionsFactoryService { get; }
            public IVsEditorAdaptersFactoryService VsEditorAdaptersFactoryService { get; }
        }

        private static IVsTextLines GetTextLines(string contentTypeAsString, string content)
        {
            var contentType = VsServices.ContentTypeRegistryService.GetContentType(contentTypeAsString);
            var vsTextBuffer = VsServices.VsEditorAdaptersFactoryService.CreateVsTextBufferAdapter(OleServiceProvider, contentType);
            vsTextBuffer.InitializeContent(content, content.Length);
            return vsTextBuffer as IVsTextLines;
        }

        private static IVsCodeWindow GetCodeWindow(bool hostControl)
        {
            var vsCodeWindow = VsServices.VsEditorAdaptersFactoryService.CreateVsCodeWindowAdapter(OleServiceProvider);
            if (hostControl)
            {
                ((IVsCodeWindowEx)(vsCodeWindow)).Initialize(
                    (uint)_codewindowbehaviorflags.CWB_DISABLESPLITTER | (uint)_codewindowbehaviorflags.CWB_DISABLEDROPDOWNBAR | (uint)_codewindowbehaviorflags2.CWB_DISABLEDIFF,
                    VSUSERCONTEXTATTRIBUTEUSAGE.VSUC_Usage_Filter,
                    string.Empty,
                    string.Empty,
                    0,
                    new INITVIEW[1]
                );
            }
            return vsCodeWindow;
        }

        private static (object, IVsTextView) GetHostControlAndVsTextView(IVsCodeWindow vsCodeWindow)
        {
            vsCodeWindow.GetPrimaryView(out var vsTextView);
            var host = VsServices.VsEditorAdaptersFactoryService.GetWpfTextViewHost(vsTextView);
            return (host.HostControl, vsTextView);
        }


        // https://developercommunity.visualstudio.com/t/projectionbuffertutorial-gives-error-in-dev16/498617
        public static (object,IVsCodeWindow,IVsTextView) GetRequired(string contentTypeAsString,string content,bool hostControl)
        {
            var vsTextLines = GetTextLines(contentTypeAsString, content);

            var vsCodeWindow = GetCodeWindow(true);
            ErrorHandler.ThrowOnFailure(vsCodeWindow.SetBuffer(vsTextLines));
            IVsTextView vsTextView = null;
            object control;
            if (hostControl)
            {
                var (ctrl,tv) = GetHostControlAndVsTextView(vsCodeWindow);
                control = ctrl;
                vsTextView = tv;
            }
            else
            {
                control = ((WindowPane)vsCodeWindow).Content;
            }


            return (control, vsCodeWindow,vsTextView);
        }
    }

The example that uses a ToolWindowPane with IOleCommandTarget passes on to the IVsTextView.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants