Skip to content

Commit

Permalink
Work around dotnet/wpf#122 in Document Outline
Browse files Browse the repository at this point in the history
  • Loading branch information
sharwell committed May 22, 2024
1 parent ea35f7c commit e65a990
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -77,22 +77,16 @@
</Grid.RowDefinitions>
<ContentControl Grid.Row="0" Name="Commands" Focusable="True" />
<Grid Grid.Row="1" x:Name="SearchHost" />
<!-- Two important properties are being set for TreeView:
We set IsVirtualizing to "True" so that WPF only generates internal data-structures elements that are visible.
Setting VirtualizationMode to "Recycling" ensures that WPF internal data is reused as items scroll in and out of view.
-->
<!-- Note: we register TreeViewItem.SourceUpdated and set `NotifyOnSourceUpdated=True, NotifyOnTargetUpdated=False`
for the item `IsSelected` event. This is important for a reentrancy issue we would otherwise have. Specifically,
As the user moves around, we want to expand/select the correct item of interest in the tree. However, we do not
want that *programmatic* selection to then end up firing an event saying "i was selected" which we then interpret
to mean that we should navigate to that item. By only hearing when the source is updated (meaning the user actually
clicked on the tree view item, not that we programmatically set it) we only actual perform the navigation on user
interaction. -->
<TreeView Grid.Row="2"
Name="SymbolTree"
<self:VirtualizingTreeView Grid.Row="2"
x:Name="SymbolTree"
AutomationProperties.Name="{x:Static self:DocumentOutlineStrings.Document_Outline}"
VirtualizingStackPanel.IsVirtualizing="True"
VirtualizingStackPanel.VirtualizationMode="Recycling"
SourceUpdated="SymbolTree_SourceUpdated"
TreeViewItem.Selected="SymbolTreeItem_Selected"
Visibility="{Binding Visibility, Mode=OneWayToSource}"
Expand Down Expand Up @@ -130,6 +124,6 @@
<Setter Property="Margin" Value="0, 0, 0, 1" />
</Style>
</TreeView.ItemContainerStyle>
</TreeView>
</self:VirtualizingTreeView>
</Grid>
</UserControl>
49 changes: 49 additions & 0 deletions src/VisualStudio/Core/Def/DocumentOutline/VirtualizingTreeView.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// 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.

using System;
using System.Windows.Automation.Peers;
using System.Windows.Controls;

namespace Microsoft.VisualStudio.LanguageServices.DocumentOutline;

// Provide a workaround for https://github.com/dotnet/wpf/issues/122 when using virtualized TreeView.
internal sealed class VirtualizingTreeView : TreeView
{
public VirtualizingTreeView()
{
// Two important properties are being set for TreeView:
// We set IsVirtualizing to "true" so that WPF only generates internal data-structures elements that are visible.
// Setting VirtualizationMode to "Recycling" ensures that WPF internal data is reused as items scroll in and out of view.
VirtualizingPanel.SetIsVirtualizing(this, true);
VirtualizingPanel.SetVirtualizationMode(this, VirtualizationMode.Recycling);
}

protected override AutomationPeer OnCreateAutomationPeer()
=> new VirtualizingTreeViewAutomationPeer(this);

public sealed class VirtualizingTreeViewAutomationPeer(TreeView owner)
: TreeViewAutomationPeer(owner)
{
protected override ItemAutomationPeer CreateItemAutomationPeer(object item)
=> new VirtualizingTreeViewDataItemAutomationPeer(item, this, null);
}

public sealed class VirtualizingTreeViewDataItemAutomationPeer(object item, ItemsControlAutomationPeer itemsControlAutomationPeer, TreeViewDataItemAutomationPeer? parentDataItemAutomationPeer)
: TreeViewDataItemAutomationPeer(item, itemsControlAutomationPeer, parentDataItemAutomationPeer)
{
protected override string GetNameCore()
{
try
{
return base.GetNameCore();
}
catch (NullReferenceException)
{
// https://github.com/dotnet/wpf/issues/122
return "";
}
}
}
}

0 comments on commit e65a990

Please sign in to comment.