Skip to content

Commit 957bbff

Browse files
authored
Merge pull request #6240 from tommy9/TestExplorerUI
Test explorer UI speed ups
2 parents 9ad77b1 + c9d98ec commit 957bbff

File tree

3 files changed

+160
-5
lines changed

3 files changed

+160
-5
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
using System;
2+
using System.ComponentModel;
3+
using System.Globalization;
4+
using System.Windows;
5+
using System.Windows.Data;
6+
using System.Windows.Markup;
7+
8+
namespace Rubberduck.UI.UnitTesting
9+
{
10+
11+
// Source: https://stackoverflow.com/a/48202247/1419315
12+
[MarkupExtensionReturnType(typeof(object))]
13+
public class LazyBindingExtension : MarkupExtension
14+
{
15+
public LazyBindingExtension()
16+
{ }
17+
18+
public LazyBindingExtension(PropertyPath path) : this()
19+
{
20+
Path = path;
21+
}
22+
23+
#region Properties
24+
25+
public IValueConverter Converter { get; set; }
26+
[TypeConverter(typeof(CultureInfoIetfLanguageTagConverter))]
27+
public CultureInfo ConverterCulture { get; set; }
28+
public object ConverterParameter { get; set; }
29+
public string ElementName { get; set; }
30+
[ConstructorArgument("path")]
31+
public PropertyPath Path { get; set; }
32+
public RelativeSource RelativeSource { get; set; }
33+
public object Source { get; set; }
34+
public UpdateSourceTrigger UpdateSourceTrigger { get; set; }
35+
public bool ValidatesOnDataErrors { get; set; }
36+
public bool ValidatesOnExceptions { get; set; }
37+
public bool ValidatesOnNotifyDataErrors { get; set; }
38+
39+
private Binding binding;
40+
private UIElement bindingTarget;
41+
private DependencyProperty bindingTargetProperty;
42+
43+
#endregion
44+
45+
#region Init
46+
47+
public override object ProvideValue(IServiceProvider serviceProvider)
48+
{
49+
var valueProvider = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget;
50+
if (valueProvider == null)
51+
{
52+
return null;
53+
}
54+
bindingTarget = valueProvider.TargetObject as UIElement;
55+
56+
if (bindingTarget == null)
57+
{
58+
throw new NotSupportedException($"The target must be a UIElement, '{valueProvider.TargetObject}' is not valid.");
59+
}
60+
61+
bindingTargetProperty = valueProvider.TargetProperty as DependencyProperty;
62+
63+
if (bindingTargetProperty == null)
64+
{
65+
throw new NotSupportedException($"The target property must be a DependencyProperty, '{valueProvider.TargetProperty}' is not valid.");
66+
}
67+
68+
binding = new Binding
69+
{
70+
Path = Path,
71+
Converter = Converter,
72+
ConverterCulture = ConverterCulture,
73+
ConverterParameter = ConverterParameter
74+
};
75+
76+
if (ElementName != null)
77+
{
78+
binding.ElementName = ElementName;
79+
}
80+
81+
if (RelativeSource != null)
82+
{
83+
binding.RelativeSource = RelativeSource;
84+
}
85+
86+
if (Source != null)
87+
{
88+
binding.Source = Source;
89+
}
90+
91+
binding.UpdateSourceTrigger = UpdateSourceTrigger;
92+
binding.ValidatesOnDataErrors = ValidatesOnDataErrors;
93+
binding.ValidatesOnExceptions = ValidatesOnExceptions;
94+
binding.ValidatesOnNotifyDataErrors = ValidatesOnNotifyDataErrors;
95+
96+
return SetBinding();
97+
}
98+
99+
100+
public object SetBinding()
101+
{
102+
bindingTarget.IsVisibleChanged += UiElement_IsVisibleChanged;
103+
104+
UpdateBinding();
105+
106+
return bindingTarget.GetValue(bindingTargetProperty);
107+
}
108+
109+
#endregion
110+
111+
#region Event Handlers
112+
113+
private void UiElement_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
114+
{
115+
UpdateBinding();
116+
}
117+
118+
#endregion
119+
120+
#region Update Binding
121+
122+
private void UpdateBinding()
123+
{
124+
if (bindingTarget.IsVisible && !IsBindingActive())
125+
ApplyBinding();
126+
else if (!bindingTarget.IsVisible)
127+
ClearBinding();
128+
}
129+
130+
private bool IsBindingActive()
131+
{
132+
return BindingOperations.GetBinding(bindingTarget, bindingTargetProperty) != null;
133+
}
134+
135+
private void ApplyBinding()
136+
{
137+
if (!IsBindingActive())
138+
BindingOperations.SetBinding(bindingTarget, bindingTargetProperty, binding);
139+
}
140+
141+
private void ClearBinding()
142+
{
143+
if (IsBindingActive())
144+
BindingOperations.ClearBinding(bindingTarget, bindingTargetProperty);
145+
}
146+
147+
#endregion
148+
}
149+
}

Rubberduck.Core/UI/UnitTesting/TestExplorerControl.xaml

+7-5
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
xmlns:controls="clr-namespace:Rubberduck.UI.Controls"
99
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
1010
xmlns:converters="clr-namespace:Rubberduck.UI.Converters"
11+
xmlns:b="http://schemas.microsoft.com/xaml/behaviors"
1112
Language="{UICulture}"
1213
mc:Ignorable="d"
1314
d:DesignHeight="400" d:DesignWidth="800"
@@ -334,17 +335,18 @@
334335
<Border Grid.Row="3" Padding="2">
335336
<Grid>
336337
<ScrollViewer VerticalScrollBarVisibility="Disabled" HorizontalScrollBarVisibility="Auto">
337-
<controls:GroupingGrid x:Name="TestGrid"
338-
ItemsSource="{Binding Tests}"
338+
<controls:GroupingGrid x:Name="TestGrid"
339+
ItemsSource="{local:LazyBinding Tests}"
339340
SelectionMode="Extended"
340341
ShowGroupingItemCount="True"
341342
InitialExpandedState="True"
342343
VirtualizingPanel.IsVirtualizingWhenGrouping="True"
343344
RequestBringIntoView="TestGrid_RequestBringIntoView"
344-
ScrollViewer.CanContentScroll="False"
345+
ScrollViewer.CanContentScroll="True"
346+
Visibility="{Binding Model.IsBusyOrRefreshing, Converter={StaticResource BoolToHiddenVisibility}}"
345347
ScrollViewer.VerticalScrollBarVisibility="Auto"
346348
ScrollViewer.HorizontalScrollBarVisibility="Disabled">
347-
<DataGrid.Columns>
349+
<DataGrid.Columns>
348350
<DataGridTemplateColumn Header="{Resx ResxName=Rubberduck.Resources.UnitTesting.TestExplorer, Key=TestExplorer_Outcome}">
349351
<DataGridTemplateColumn.CellTemplate>
350352
<DataTemplate DataType="vm:TestMethodViewModel">
@@ -480,6 +482,6 @@
480482
</ScrollViewer>
481483
</Grid>
482484
</Border>
483-
<controls:BusyIndicator Grid.Row="3" Width="120" Height="120" Visibility="{Binding Model.IsRefreshing, Converter={StaticResource BoolToVisibility}}" />
485+
<controls:BusyIndicator Grid.Row="3" Width="120" Height="120" Visibility="{Binding Model.IsBusyOrRefreshing, Converter={StaticResource BoolToVisibility}}" />
484486
</Grid>
485487
</UserControl>

Rubberduck.Core/UI/UnitTesting/TestExplorerModel.cs

+4
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ public bool IsBusy
8383
{
8484
_isBusy = value;
8585
OnPropertyChanged();
86+
OnPropertyChanged(nameof(IsBusyOrRefreshing));
8687
}
8788
}
8889

@@ -94,9 +95,12 @@ public bool IsRefreshing
9495
{
9596
_isRefreshing = value;
9697
OnPropertyChanged();
98+
OnPropertyChanged(nameof(IsBusyOrRefreshing));
9799
}
98100
}
99101

102+
public bool IsBusyOrRefreshing => IsBusy || IsRefreshing;
103+
100104
public string LastTestRunSummary =>
101105
string.Format(Resources.UnitTesting.TestExplorer.TestOutcome_RunSummaryFormat, CurrentRunTestCount, Tests.Count, TimeSpan.FromMilliseconds(TotalDuration));
102106

0 commit comments

Comments
 (0)