Community.VisualStudio.Toolkit icon indicating copy to clipboard operation
Community.VisualStudio.Toolkit copied to clipboard

How to add Toolbar and buttons to the Toolbar in a ToolWindow

Open ghost opened this issue 2 years ago • 3 comments

@reduckted, @madskristensen Using the Toolkit how do you add a toolbar and buttons (with icons) on the toolbar to the Toolwindow you created with the Community.VisualStudio.Toolkit?

Using the following MyToolWindowControl.xaml.cs file the Toolwindow has the toolbar and buttons in the toolbar, but the button click event do not fire. Also, the theme is not set on the toolbar or buttons as it is in the Toolwindow.

This MS Documentation link does not work either Add a toolbar to a tool window since the example code was not using the Toolkit and (files names or similar files) listed do not exist when using the Toolkit created Toolwindow and failed to build because the reference Toolbar and GUID did not exist. Where would you add these from that link:

image

I would like the Toolbar Buttons to work and look like Solution Explorer Toolbar and Buttons.

Suggest it as a new show for the Hacking Visual Studio show

using System.Linq;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Navigation;
using System.Windows.Shapes;
using HelpExplorer.Schema;
using Microsoft.Internal.VisualStudio.PlatformUI;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.Utilities;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;

namespace HelpExplorer
{
    public partial class MyToolWindowControl : UserControl
    {
        private readonly ProjectTypeCollection _projectTypes;
        private readonly FileTypeCollection _fileTypes;
        public IVsHierarchy Hierarchy = null;
        public string CapabilityValues = null;
        public Project _activeProject;
        public string _activeFile;
        public ToolBar TWToolbar = new ToolBar();

        public MyToolWindowControl(ProjectTypeCollection projectTypes, FileTypeCollection fileTypes, Project activeProject)
        {
            ThreadHelper.ThrowIfNotOnUIThread();

            _projectTypes = projectTypes;
            _fileTypes = fileTypes;
            _activeProject = activeProject;
            InitializeComponent();
            CreateToolBarButtons(TWToolbar);
            GetActiveProjectCapabilities(activeProject);
            UpdateProjects(Hierarchy);

            VS.Events.SelectionEvents.SelectionChanged += SelectionChanged;
            VS.Events.DocumentEvents.BeforeDocumentWindowShow += BeforeDocumentWindowShow;
            VS.Events.SolutionEvents.OnAfterCloseSolution += OnAfterCloseSolution;
        }

        private void BeforeDocumentWindowShow(DocumentView docView)
        {
            ThreadHelper.JoinableTaskFactory.RunAsync(async () =>
            {
                if (docView?.Document?.FilePath != _activeFile)
                {
                    _activeFile = docView?.Document?.FilePath;
                    var ext = System.IO.Path.GetExtension(docView?.Document?.FilePath);
                    await UpdateFilesAsync(ext);
                }

            }).FireAndForget();
        }

        private void OnAfterCloseSolution()
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            SelectionChanged(null, null);
        }

        private void GetActiveProjectCapabilities(Project activeProject)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            if (activeProject != null)
            {
                activeProject.GetItemInfo(out IVsHierarchy hierarchy, out var itemId, out IVsHierarchyItem item);
                HierarchyUtilities.TryGetHierarchyProperty<string>(hierarchy, itemId, (int)__VSHPROPID5.VSHPROPID_ProjectCapabilities, out var capabilityValues);
                Hierarchy = hierarchy;
                CapabilityValues = capabilityValues;
            }
        }
        private void WriteCapabilitiesToFile(string capability, string fileName)
        {
            var capabilities = (capability ?? "").Split(' ');
            var dir = General.Instance.CapabilitiesFilePathOption;
            var file = System.IO.Path.Combine(dir, $"{fileName}_Capabilities.txt");
            if (!System.IO.Directory.Exists(dir))
            {
                System.IO.Directory.CreateDirectory(dir);
            }
            if (System.IO.File.Exists(file))
            {
                System.IO.File.Delete(file);
                System.IO.File.WriteAllLines(file, capabilities);
            }
            else
            {
                System.IO.File.WriteAllLines(file, capabilities);
            }
        }

        private void SelectionChanged(object sender, Community.VisualStudio.Toolkit.SelectionChangedEventArgs e)
        {
            ThreadHelper.JoinableTaskFactory.RunAsync(async () =>
            {
                Project project = await VS.Solutions.GetActiveProjectAsync();

                if (project != _activeProject)
                {
                    _activeProject = project;
                    await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
                    GetActiveProjectCapabilities(project);
                    UpdateProjects(Hierarchy);
                }
            }).FireAndForget();
        }

        private async Task UpdateFilesAsync(string fileExtension)
        {
            if (fileExtension == null)
            {
                return;
            }
            await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();

            FileTypes.Children.Clear();
            foreach (FileType ft in _fileTypes.FileTypes.Where(f => fileExtension.Equals(f.Name)))
            {
                var text = new TextBlock { Text = ft.Text, TextWrapping = TextWrapping.Wrap, Margin = new Thickness(0, 0, 0, 5) };
                FileTypes.Children.Add(text);

                foreach (Link link in ft.Links)
                {
                    var h = new Hyperlink
                    {
                        NavigateUri = new Uri(link.Url)
                    };

                    h.RequestNavigate += OnRequestNavigate;
                    h.Inlines.Add(link.Text);
                    var textBlock = new TextBlock { Text = "- ", Margin = new Thickness(15, 0, 0, 0) };
                    textBlock.Inlines.Add(h);

                    FileTypes.Children.Add(textBlock);
                }

                var line = new Line { Margin = new Thickness(0, 0, 0, 20) };
                FileTypes.Children.Add(line);
                // read settings
                if (General.Instance.MultipleFilesOption)
                {
                    continue;
                }
                break;
            }
        }

        private void UpdateProjects(IVsHierarchy hierarchy)
        {
            ProjectTypes.Children.Clear();

            foreach (ProjectType pt in _projectTypes.ProjectTypes)
            {
                var capability = pt.CapabilityExpression;

                if ((_activeProject == null && !string.IsNullOrEmpty(capability)) || _activeProject != null && string.IsNullOrEmpty(capability))
                {
                    continue;
                }
                else if (!string.IsNullOrEmpty(capability) && hierarchy?.IsCapabilityMatch(capability) == false)
                {
                    continue;
                }
                if (General.Instance.CreateCapabilitiesFile)
                {
                    //The following capabilities line allows you to check the projects capabilities so they can be added to projectTypes.json.
                    if (!string.IsNullOrEmpty(CapabilityValues) && !string.IsNullOrEmpty(pt.CapabilitiesFileName))
                    {
                        WriteCapabilitiesToFile(CapabilityValues, pt.CapabilitiesFileName);
                    }
                }

                var text = new TextBlock { Text = pt.Text, TextWrapping = TextWrapping.Wrap, Margin = new Thickness(0, 0, 0, 5) };
                ProjectTypes.Children.Add(text);

                foreach (Link link in pt.Links)
                {
                    var h = new Hyperlink
                    {
                        NavigateUri = new Uri(link.Url)
                    };

                    h.RequestNavigate += OnRequestNavigate;
                    h.Inlines.Add(link.Text);
                    var textBlock = new TextBlock { Text = "- ", Margin = new Thickness(15, 0, 0, 0) };
                    textBlock.Inlines.Add(h);

                    ProjectTypes.Children.Add(textBlock);
                }

                var line = new Line { Margin = new Thickness(0, 0, 0, 20) };
                ProjectTypes.Children.Add(line);
                // read settings
                if (General.Instance.MultipleProjectsOption)
                {
                    continue;
                }
                break;
            }
        }
        private void CreateToolBarButtons(ToolBar toolBar)
        {
            if (!toolBar.HasItems)
            {
                var button1 = new Button() { Name = "MultipleProjectsTypes", Width = 25, Height = 25, Content = "MP" };
                button1.Click += MultipleProjectsTypes_Click;
                toolBar.Items.Add(button1);
                var button2 = new Button() { Name = "MultipleFileTypes", Width = 25, Height = 25, Content = "MF" };
                button2.Click += MultipleFileTypes_Click;
                toolBar.Items.Add(button2);
                var button3 = new Button() { Name = "CreateCapabilitesFile", Width = 25, Height = 25, Content = "CC" };
                button3.Click += CreateCapabilitesFile_Click;
                toolBar.Items.Add(button3);
            }
        }

        private void OnRequestNavigate(object sender, RequestNavigateEventArgs e)
        {
            VsShellUtilities.OpenBrowser(e.Uri.AbsoluteUri, (int)__VSOSPFLAGS.OSP_LaunchSingleBrowser);
            e.Handled = true;
        }

        private void MultipleProjectsTypes_Click(object sender, RoutedEventArgs e)
        {
            if (General.Instance.MultipleProjectsOption)
            {
                General.Instance.MultipleProjectsOption = false;
                General.Instance.Save();
            }
            else
            {
                General.Instance.MultipleProjectsOption = true;
                General.Instance.Save();
            }
            e.Handled = true;
        }

        private void MultipleFileTypes_Click(object sender, RoutedEventArgs e)
        {
            if (General.Instance.MultipleFilesOption)
            {
                General.Instance.MultipleFilesOption = false;
                General.Instance.Save();
            }
            else
            {
                General.Instance.MultipleFilesOption = true;
                General.Instance.Save();
            }
            e.Handled = true;
        }


        private void CreateCapabilitesFile_Click(object sender, RoutedEventArgs e)
        {
            if (General.Instance.CreateCapabilitiesFile)
            {
                General.Instance.CreateCapabilitiesFile = false;
                General.Instance.Save();
            }
            else
            {
                General.Instance.CreateCapabilitiesFile = true;
                General.Instance.Save();
            }
            e.Handled = true;
        }
    }
}

ghost avatar Dec 09 '21 14:12 ghost

I've actually never added a toolbar to a window. It would be good to take a look at what's required to do the right way and then simplify it, if needed, through the toolkit

madskristensen avatar Dec 09 '21 17:12 madskristensen

I've actually done this before! Not with the toolkit though, but it should be pretty easy.

In your BaseToolWindow implementation, there will be a Pane class. In the constructor of the Pane class, you can set the toolbar.

As an example, in the demo extension, if we defined a toolbar in the .vsct file the same way that it's defined in the sample code, then we could add the toolbar to the RunnerWindow by making this change:

         [Guid("d3b3ebd9-87d1-41cd-bf84-268d88953417")]
         internal class Pane : ToolWindowPane
         {
             public Pane()
             {
                 BitmapImageMoniker = KnownMonikers.StatusInformation;
+                Toolbar = new CommandID(new Guid(TWTestCommandPackageGuids.guidTWTestCommandPackageCmdSet), TWTestCommandPackageGuids.TWToolbar);
             }
         }

The important thing to remember is the buttons on the toolbar are no different to the buttons on standard Visual Studio toolbars or in Visual Studio menus. In other words, they are just commands and need to be handled the same way.

reduckted avatar Dec 10 '21 01:12 reduckted

I've created pull request #242 which demonstrates how you can add a toolbar to a tool window.

reduckted avatar Dec 10 '21 11:12 reduckted