File: Utilities\AutomationDelegatingListView.cs
Web Access
Project: src\src\VisualStudio\Core\Def\Microsoft.VisualStudio.LanguageServices_ozsccwvc_wpftmp.csproj (Microsoft.VisualStudio.LanguageServices)
// 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.
 
extern alias slowautomation;
 
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using slowautomation::System.Windows.Automation;
using System.Windows.Automation.Peers;
using System.Windows.Controls;
using System.Windows.Input;
using Roslyn.Utilities;
 
namespace Microsoft.VisualStudio.LanguageServices.Implementation.Utilities;
 
internal class AutomationDelegatingListView : ListView
{
    protected override bool IsItemItsOwnContainerOverride(object item)
        => item is AutomationDelegatingListViewItem;
 
    protected override DependencyObject GetContainerForItemOverride()
        => new AutomationDelegatingListViewItem();
 
    protected override AutomationPeer OnCreateAutomationPeer()
        => new AutomationDelegatingListViewAutomationPeer(this);
 
    protected override void OnGotKeyboardFocus(KeyboardFocusChangedEventArgs e)
    {
        base.OnGotKeyboardFocus(e);
        if (this.SelectedIndex == -1)
        {
            this.SelectedIndex = 0;
        }
    }
}
 
internal class AutomationDelegatingListViewAutomationPeer : FrameworkElementAutomationPeer
{
    public AutomationDelegatingListViewAutomationPeer(AutomationDelegatingListView listView)
        : base(listView)
    {
    }
 
    protected override List<AutomationPeer>? GetChildrenCore()
    {
        List<AutomationPeer>? results = null;
        var peersToProcess = new Queue<AutomationPeer>(base.GetChildrenCore() ?? SpecializedCollections.EmptyEnumerable<AutomationPeer>());
        while (peersToProcess.Count > 0)
        {
            var peer = peersToProcess.Dequeue();
            if (peer is ListBoxItemWrapperAutomationPeer itemWrapperAutomationPeer)
            {
                results ??= [];
                results.Add(itemWrapperAutomationPeer);
            }
            else
            {
                foreach (var childPeer in peer.GetChildren() ?? SpecializedCollections.EmptyEnumerable<AutomationPeer>())
                {
                    peersToProcess.Enqueue(childPeer);
                }
            }
        }
 
        return results;
    }
 
    protected override AutomationControlType GetAutomationControlTypeCore()
        => AutomationControlType.List;
}
 
internal class AutomationDelegatingListViewItem : ListViewItem
{
    protected override AutomationPeer OnCreateAutomationPeer()
        => new AutomationDelegatingListViewItemAutomationPeer(this);
}
 
internal class AutomationDelegatingListViewItemAutomationPeer : ListBoxItemWrapperAutomationPeer
{
    private readonly CheckBoxAutomationPeer? checkBoxItem;
    private readonly RadioButtonAutomationPeer? radioButtonItem;
    private readonly TextBlockAutomationPeer? textBlockItem;
 
    public AutomationDelegatingListViewItemAutomationPeer(AutomationDelegatingListViewItem listViewItem)
        : base(listViewItem)
    {
        checkBoxItem = this.GetChildren()?.OfType<CheckBoxAutomationPeer>().SingleOrDefault();
        if (checkBoxItem != null)
        {
            var toggleButton = ((CheckBox)checkBoxItem.Owner);
            toggleButton.Checked += Checkbox_CheckChanged;
            toggleButton.Unchecked += Checkbox_CheckChanged;
            return;
        }
 
        radioButtonItem = this.GetChildren()?.OfType<RadioButtonAutomationPeer>().SingleOrDefault();
        if (radioButtonItem != null)
        {
            var toggleButton = ((RadioButton)radioButtonItem.Owner);
            toggleButton.Checked += RadioButton_CheckChanged;
            toggleButton.Unchecked += RadioButton_CheckChanged;
            return;
        }
 
        textBlockItem = this.GetChildren()?.OfType<TextBlockAutomationPeer>().FirstOrDefault();
    }
 
    private void Checkbox_CheckChanged(object sender, RoutedEventArgs e)
    {
        var checkBox = (CheckBox)sender;
        RaisePropertyChangedEvent(
            TogglePatternIdentifiers.ToggleStateProperty,
            oldValue: ConvertToToggleState(!checkBox.IsChecked),
            newValue: ConvertToToggleState(checkBox.IsChecked));
    }
 
    private void RadioButton_CheckChanged(object sender, RoutedEventArgs e)
    {
        // RadioButtonAutomationPeer sets oldValue and newValue to true, so we do the same here
        // See http://referencesource.microsoft.com/#PresentationFramework/src/Framework/System/Windows/Automation/Peers/RadioButtonAutomationPeer.cs,114
        RaisePropertyChangedEvent(
            SelectionItemPatternIdentifiers.IsSelectedProperty,
            oldValue: true,
            newValue: true);
    }
 
    private static ToggleState ConvertToToggleState(bool? value)
    {
        switch (value)
        {
            case true: return ToggleState.On;
            case false: return ToggleState.Off;
            default: return ToggleState.Indeterminate;
        }
    }
 
    protected override AutomationControlType GetAutomationControlTypeCore()
    {
        if (checkBoxItem != null)
        {
            return AutomationControlType.CheckBox;
        }
        else if (radioButtonItem != null)
        {
            return AutomationControlType.RadioButton;
        }
        else
        {
            return AutomationControlType.Text;
        }
    }
 
    public override object? GetPattern(PatternInterface patternInterface)
    {
        var automationPeer = GetAutomationPeer();
        return automationPeer != null
            ? automationPeer.GetPattern(patternInterface)
            : base.GetPattern(patternInterface);
    }
 
    protected override string GetNameCore()
        => GetAutomationPeer()?.GetName() ?? string.Empty;
 
    private AutomationPeer? GetAutomationPeer()
        => checkBoxItem ?? radioButtonItem ?? (AutomationPeer?)textBlockItem;
}