File: Options\AbstractOptionPage.cs
Web Access
Project: src\src\VisualStudio\Core\Impl\Microsoft.VisualStudio.LanguageServices.Implementation.csproj (Microsoft.VisualStudio.LanguageServices.Implementation)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
#nullable disable
 
using System;
using System.Collections.Generic;
using Microsoft.CodeAnalysis.Options;
using Microsoft.VisualStudio.ComponentModelHost;
using Microsoft.VisualStudio.Shell;
using Roslyn.Utilities;
 
namespace Microsoft.VisualStudio.LanguageServices.Implementation.Options;
 
[System.ComponentModel.DesignerCategory("code")] // this must be fully qualified
internal abstract class AbstractOptionPage : UIElementDialogPage
{
    private static OptionStore s_optionStore;
    private static bool s_needsToUpdateOptionStore = true;
 
    private bool _needsLoadOnNextActivate = true;
 
    protected abstract AbstractOptionPageControl CreateOptionPage(IServiceProvider serviceProvider, OptionStore optionStore);
 
    protected AbstractOptionPageControl pageControl;
 
    private void EnsureOptionPageCreated()
    {
        if (s_optionStore == null)
        {
            var componentModel = (IComponentModel)this.Site.GetService(typeof(SComponentModel));
            s_optionStore = new OptionStore(componentModel.GetService<IGlobalOptionService>());
        }
 
        // Use a single option store for all option pages so that changes are accumulated
        // together and, in the case of the same option appearing on two pages, the changes
        // are kept in sync.
        pageControl ??= CreateOptionPage(this.Site, s_optionStore);
    }
 
    protected override System.Windows.UIElement Child
    {
        get
        {
            EnsureOptionPageCreated();
            return pageControl;
        }
    }
 
    protected override void OnActivate(System.ComponentModel.CancelEventArgs e)
    {
        EnsureOptionPageCreated();
 
        if (s_needsToUpdateOptionStore)
        {
            // Reset the option store to the current state of the options.
            s_optionStore.Clear();
 
            s_needsToUpdateOptionStore = false;
        }
 
        if (_needsLoadOnNextActivate)
        {
            // For pages that don't use option bindings we need to load setting changes.
            pageControl.OnLoad();
 
            _needsLoadOnNextActivate = false;
        }
    }
 
    public override void LoadSettingsFromStorage()
    {
        // This gets called in two situations:
        //
        // 1) during the initial page load when you first activate the page, before OnActivate
        //    is called.
        // 2) during the closing of the dialog via Cancel/close when options don't need to be
        //    saved. The intent here is the settings get reloaded so the next time you open the
        //    page they are properly populated.
        //
        // We need to ignore the first case since the option store is static and shared among 
        // pages. Each page will get this same call so we should ensure that our page has been
        // created first.
        //
        // This second one is tricky, because we don't actually want to update our controls
        // right then, because they'd be wrong the next time the page opens -- it's possible
        // they may have been changed programmatically. Therefore, we'll set a flag so we load
        // next time
 
        // When pageControl is null we know that Activation has not happened for this page.
        // We only need to update the OptionStore after a cancel or close click (2).
        s_needsToUpdateOptionStore = pageControl != null;
 
        // For pages that don't use option bindings we need to load settings when it is
        // activated next.
        _needsLoadOnNextActivate = true;
    }
 
    public override void SaveSettingsToStorage()
    {
        EnsureOptionPageCreated();
 
        // Allow page controls to perist their settings to the option store before updating the
        // option service.
        pageControl.OnSave();
 
        var changedOptions = s_optionStore.GetChangedOptions();
        OptionLogger.Log(changedOptions);
 
        s_optionStore.GlobalOptions.SetGlobalOptions(changedOptions.SelectAsArray(entry => KeyValuePair.Create(entry.key, entry.newValue)));
 
        // Make sure we load the next time a page is activated, in case that options changed
        // programmatically between now and the next time the page is activated
        s_needsToUpdateOptionStore = true;
 
        // For pages that don't use option bindings we need to load settings when it is
        // activated next.
        _needsLoadOnNextActivate = true;
    }
 
    protected override void SearchStringChanged(string searchString)
    {
        pageControl.OnSearch(searchString);
    }
 
    protected override void OnClosed(EventArgs e)
    {
        base.OnClosed(e);
 
        if (pageControl != null)
        {
            // Clear the search because we don't recreate controls
            pageControl.OnSearch(string.Empty);
            pageControl.Close();
        }
    }
}