Skip to content

Commit

Permalink
Simplify the TreeView.SelectedItem binding logic.
Browse files Browse the repository at this point in the history
 There were many situations where the code would fail, it also accessed
 private methods.

 Automating the ScrollViewer seems much simpler than messing around with
 the ItemContainerGenerator internals.
  • Loading branch information
pudding committed Jan 28, 2024
1 parent 17110f8 commit d8c9f1b
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 269 deletions.
11 changes: 11 additions & 0 deletions FoxTunes.UI.Windows/Extensions/Extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Interop;
Expand Down Expand Up @@ -312,5 +313,15 @@ public static WriteableBitmap Resize(this WriteableBitmap source, Size size)
target.Render(visual);
return new WriteableBitmap(target);
}

public static void ScrollToItemOffset<T>(this ScrollViewer scrollViewer, int offset) where T : FrameworkElement
{
var item = scrollViewer.FindChild<T>();
if (item == null)
{
return;
}
scrollViewer.ScrollToVerticalOffset(offset * item.ActualHeight);
}
}
}
199 changes: 0 additions & 199 deletions FoxTunes.UI.Windows/Extensions/TreeView_EnsureSelectedItemVisible.cs

This file was deleted.

93 changes: 24 additions & 69 deletions FoxTunes.UI.Windows/Extensions/TreeView_SelectedItem.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using FoxTunes.Interfaces;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Controls;
Expand Down Expand Up @@ -46,25 +45,6 @@ private static void OnSelectedItemPropertyChanged(DependencyObject sender, Depen

private class SelectedItemBehaviour : UIBehaviour
{
private static readonly PropertyInfo ItemsHost = typeof(ItemsControl).GetProperty(
"ItemsHost",
BindingFlags.Instance | BindingFlags.NonPublic
);

private static readonly MethodInfo EnsureGenerator = typeof(Panel).GetMethod(
"EnsureGenerator",
BindingFlags.Instance | BindingFlags.NonPublic
);

#if NET40

private static readonly MethodInfo BringIndexIntoView = typeof(VirtualizingPanel).GetMethod(
"BringIndexIntoView",
BindingFlags.Instance | BindingFlags.NonPublic
);

#endif

public SelectedItemBehaviour(TreeView treeView)
{
this.TreeView = treeView;
Expand Down Expand Up @@ -126,73 +106,48 @@ protected virtual void Select(Stack<IHierarchical> stack)
}
if (item != null)
{
//If we had a previous item then expand it.
item.IsExpanded = true;
items = item;
}
else
{
//Else it's the first iteration.
items = this.TreeView;
}
//Try the easy method.
item = items.ItemContainerGenerator.ContainerFromItem(value) as TreeViewItem;
item = this.BringIntoView(items, value);
if (item == null)
{
//Looks like the item hasn't been generated.
//Apply any templates we might need.
if (items.Template != null)
{
items.ApplyTemplate();
var presenter = items.Template.FindName("ItemsHost", items) as ItemsPresenter;
if (presenter != null)
{
presenter.ApplyTemplate();
}
}
//Update the layout and get the panel.
items.UpdateLayout();
var panel = ItemsHost.GetValue(items, null) as VirtualizingPanel;
if (panel != null)
{
//Enssure the ItemContainerGenerator is constructed.
EnsureGenerator.Invoke(panel, null);
//Get the index of the value.
var index = items.Items.IndexOf(value);
if (index < 0)
{
//There is no item corresponding to the current value.
//Nothing can be done.
break;
}
//Tell the panel to being the index into view.
//This will create the item we're looking for.
#if NET40
BringIndexIntoView.Invoke(panel, new object[] { index });
#else
panel.BringIndexIntoViewPublic(index);
#endif
}
//Try the easy method (again).
item = items.ItemContainerGenerator.ContainerFromItem(value) as TreeViewItem;
if (item == null)
{
//Looks like the item hasn't been generated.
//Nothing can be done.
break;
}
return;
}
} while (stack.Count > 0);
if (item != null && !item.IsSelected)
{
item.IsSelected = true;
}
}

protected virtual TreeViewItem BringIntoView(ItemsControl items, object value)
{
var index = items.Items.IndexOf(value);
if (index < 0)
{
return null;
}
var item = items.ItemContainerGenerator.ContainerFromItem(value) as TreeViewItem;
if (item != null)
{
//Found the item, select it.
item.IsSelected = true;
item.BringIntoView();
}
else
{

var scrollViewer = items.FindChild<ScrollViewer>();
if (scrollViewer != null)
{
scrollViewer.ScrollToItemOffset<TreeViewItem>(index);
items.UpdateLayout();
item = items.ItemContainerGenerator.ContainerFromItem(value) as TreeViewItem;
}
}
return item;
}

protected virtual void OnSelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
Expand Down
1 change: 0 additions & 1 deletion FoxTunes.UI.Windows/LibraryTree.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@
<TreeView x:Name="TreeView" Margin="0,2,0,2"
ItemsSource="{Binding Items, Source={StaticResource ViewModel}, IsAsync=True}"
Windows:TreeViewExtensions.SelectedItem="{Binding SelectedItem, Source={StaticResource ViewModel}}"
Windows:TreeViewExtensions.EnsureSelectedItemVisible="True"
Windows:TreeViewExtensions.RightButtonSelect="True"
Windows:TreeViewExtensions.DragSource="True"
Windows:TreeViewExtensions.DragSourceInitialized="DragSourceInitialized"
Expand Down

0 comments on commit d8c9f1b

Please sign in to comment.