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

Work around crash in Document Outline #73564

Merged
merged 1 commit into from
May 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.
-->
sharwell marked this conversation as resolved.
Show resolved Hide resolved
<!-- 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>
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 "";
}
}
}
}
Loading