Skip to content

Commit 3d56741

Browse files
committed
Fix TreeListViewItems IsExpanded bug
When IsExpanded is data bound and the bound object defaults to true, the TreeListViewItem will now properly start in an expanded state. Fix issue with referencing the TreeListView from UI Automation by adding in a new AutomationPeer. Fixed issue with UI Test artifacts location on pipeline.
1 parent cb08b95 commit 3d56741

File tree

11 files changed

+113
-18
lines changed

11 files changed

+113
-18
lines changed

.github/workflows/build_artifacts.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ jobs:
5656
uses: actions/upload-artifact@v3
5757
with:
5858
name: Screenshots-${{ github.run_number }}
59-
path: ${{ github.workspace }}\MaterialDesignThemes.UITests\bin\${{ inputs.build-configuration }}\net8.0-windows\Screenshots
59+
path: ${{ github.workspace }}\MaterialDesignThemes.UITests\bin\${{ inputs.build-configuration }}\net8.0-windows7\Screenshots
6060
if-no-files-found: ignore
6161

6262
- name: Build NuGets

Directory.packages.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
<PackageVersion Include="System.Net.Http" Version="4.3.4" />
2727
<PackageVersion Include="System.Text.RegularExpressions" Version="4.3.1" />
2828
<PackageVersion Include="VirtualizingWrapPanel" Version="1.5.8" />
29-
<PackageVersion Include="XAMLTest" Version="1.2.0" />
29+
<PackageVersion Include="XAMLTest" Version="1.2.1" />
3030
<PackageVersion Include="xunit" Version="2.6.2" />
3131
<PackageVersion Include="xunit.runner.visualstudio" Version="2.5.4" />
3232
<PackageVersion Include="Xunit.StaFact" Version="1.1.11" />

MaterialDesignThemes.UITests/TestBase.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,9 @@ protected async Task<IVisualElement> LoadUserControl(Type userControlType)
5050
public async Task InitializeAsync() =>
5151
App = await XamlTest.App.StartRemote(new AppOptions
5252
{
53+
#if !DEBUG
5354
MinimizeOtherWindows = !Debugger.IsAttached,
55+
#endif
5456
AllowVisualStudioDebuggerAttach = AttachedDebuggerToRemoteProcess,
5557
LogMessage = Output.WriteLine
5658
});

MaterialDesignThemes.UITests/WPF/TreeListViews/TreeItem.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
using System.ComponentModel;
22
using System.Diagnostics;
33
using System.Windows.Data;
4+
using CommunityToolkit.Mvvm.ComponentModel;
45

56
namespace MaterialDesignThemes.UITests.WPF.TreeListViews;
67

78
[DebuggerDisplay("{Value} (Children: {Children.Count})")]
8-
public class TreeItem
9+
public partial class TreeItem : ObservableObject
910
{
11+
[ObservableProperty]
12+
private bool _isExpanded;
13+
1014
public string Value { get; }
1115

1216
public TreeItem? Parent { get; }

MaterialDesignThemes.UITests/WPF/TreeListViews/TreeListViewDataBinding.xaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,15 @@
2222
<TextBlock Text="{Binding Value}" />
2323
</HierarchicalDataTemplate>
2424
</materialDesign:TreeListView.ItemTemplate>
25+
<materialDesign:TreeListView.ItemContainerStyle>
26+
<Style TargetType="materialDesign:TreeListViewItem" BasedOn="{StaticResource {x:Type materialDesign:TreeListViewItem}}">
27+
<Setter Property="IsExpanded" Value="{Binding IsExpanded}" />
28+
</Style>
29+
</materialDesign:TreeListView.ItemContainerStyle>
2530
</materialDesign:TreeListView>
2631
<StackPanel Grid.Row="1" Orientation="Horizontal">
2732
<Button Content="Add" Click="Add_OnClick" />
33+
<Button Content="Add with Children" Click="AddWithChildren_OnClick" />
2834
<Button Content="Remove" Click="Remove_OnClick" />
2935
<Button Content="Replace" Click="Replace_OnClick" />
3036
<Button Content="Down" Click="MoveDown_OnClick" />

MaterialDesignThemes.UITests/WPF/TreeListViews/TreeListViewDataBinding.xaml.cs

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ public TreeListViewDataBinding()
1919
}
2020

2121
public void Add_OnClick(object sender, EventArgs e) => AddItem();
22+
public void AddWithChildren_OnClick(object sender, EventArgs e) => AddItemWithChildren();
2223

2324
private void AddItem()
2425
{
@@ -32,6 +33,18 @@ private void AddItem()
3233
}
3334
}
3435

36+
private void AddItemWithChildren()
37+
{
38+
TreeItem parent = new(Items.Count.ToString(), null)
39+
{
40+
IsExpanded = true
41+
};
42+
parent.Children.Add(new TreeItem(parent.Value + "_0", parent));
43+
parent.Children.Add(new TreeItem(parent.Value + "_1", parent));
44+
parent.Children.Add(new TreeItem(parent.Value + "_2", parent));
45+
Items.Add(parent);
46+
}
47+
3548
public void Remove_OnClick(object sender, EventArgs e)
3649
{
3750
if (TreeListView.SelectedItem is TreeItem selectedItem)
@@ -40,12 +53,9 @@ public void Remove_OnClick(object sender, EventArgs e)
4053
}
4154
}
4255

43-
private void RemoveItem(IList<TreeItem> items, TreeItem toRemove)
56+
private static void RemoveItem(IList<TreeItem> items, TreeItem toRemove)
4457
{
45-
if (items.Contains(toRemove))
46-
{
47-
items.Remove(toRemove);
48-
}
58+
items.Remove(toRemove);
4959
foreach (TreeItem item in items)
5060
{
5161
RemoveItem(item.Children, toRemove);

MaterialDesignThemes.UITests/WPF/TreeListViews/TreeListViewTests.cs

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ public class TreeListViewTests : TestBase
77
public TreeListViewTests(ITestOutputHelper output)
88
: base(output)
99
{
10-
AttachedDebuggerToRemoteProcess = false;
10+
AttachedDebuggerToRemoteProcess = true;
1111
}
1212

1313
public static IEnumerable<object[]> GetTestControls()
@@ -74,7 +74,12 @@ await Wait.For(async () =>
7474
{
7575
childElement = await treeListView.GetElement<TreeListViewItem>("/TreeListViewItem[3]");
7676
await childElement.LeftClick();
77-
return await childElement.GetIsSelected();
77+
bool isSelected = await childElement.GetIsSelected();
78+
if (!isSelected)
79+
{
80+
await Task.Delay(MouseInput.GetDoubleClickTime);
81+
}
82+
return isSelected;
7883
});
7984
await moveDownButton.LeftClick();
8085

@@ -112,7 +117,12 @@ await Wait.For(async () =>
112117
{
113118
childElement = await treeListView.GetElement<TreeListViewItem>("/TreeListViewItem[3]");
114119
await childElement.LeftClick();
115-
return await childElement.GetIsSelected();
120+
bool isSelected = await childElement.GetIsSelected();
121+
if (!isSelected)
122+
{
123+
await Task.Delay(MouseInput.GetDoubleClickTime);
124+
}
125+
return isSelected;
116126
});
117127
await moveUpButton.LeftClick();
118128

@@ -985,6 +995,28 @@ public async Task TreeListView_WithCollectionView_RendersItems()
985995
recorder.Success();
986996
}
987997

998+
[Fact]
999+
public async Task TreeListView_AddingExpandedItemWithChildren_ShowsExpanedItem()
1000+
{
1001+
await using var recorder = new TestRecorder(App);
1002+
1003+
IVisualElement<Grid> root = (await LoadUserControl<TreeListViewDataBinding>()).As<Grid>();
1004+
IVisualElement<TreeListView> treeListView = await root.GetElement<TreeListView>();
1005+
IVisualElement<Button> addWithChildrenButton = await root.GetElement(ElementQuery.PropertyExpression<Button>(x => x.Content, "Add with Children"));
1006+
1007+
await addWithChildrenButton.LeftClick();
1008+
1009+
await AssertTreeItemContent(treeListView, 0, "0");
1010+
await AssertTreeItemContent(treeListView, 1, "1");
1011+
await AssertTreeItemContent(treeListView, 2, "2");
1012+
await AssertTreeItemContent(treeListView, 3, "3", true);
1013+
await AssertTreeItemContent(treeListView, 4, "3_0");
1014+
await AssertTreeItemContent(treeListView, 5, "3_1");
1015+
await AssertTreeItemContent(treeListView, 6, "3_2");
1016+
1017+
recorder.Success();
1018+
}
1019+
9881020
private static async Task AssertTreeItemContent(IVisualElement<TreeListView> treeListView, int index, string content, bool isExpanded = false)
9891021
{
9901022
await Wait.For(async () =>
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
using System.Windows.Automation.Peers;
2+
3+
namespace MaterialDesignThemes.Wpf.Automation.Peers;
4+
5+
public class TreeListViewAutomationPeer : ListViewAutomationPeer
6+
{
7+
public TreeListViewAutomationPeer(TreeListView owner) : base(owner)
8+
{
9+
}
10+
11+
protected override ItemAutomationPeer CreateItemAutomationPeer(object item)
12+
=> new TreeListViewItemAutomationPeer(item, this);
13+
}
14+
15+
public class TreeListViewItemAutomationPeer : ListBoxItemAutomationPeer
16+
{
17+
public TreeListViewItemAutomationPeer(object owner, SelectorAutomationPeer selectorAutomationPeer)
18+
: base(owner, selectorAutomationPeer)
19+
{
20+
}
21+
22+
public override object GetPattern(PatternInterface patternInterface)
23+
{
24+
if (patternInterface == PatternInterface.ExpandCollapse)
25+
{
26+
return this;
27+
}
28+
return base.GetPattern(patternInterface);
29+
}
30+
}

MaterialDesignThemes.Wpf/Internal/TreeListViewItemsCollection.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ public int GetLevel(int index)
5858
public bool GetIsExpanded(int index)
5959
=> ItemIsExpanded[index];
6060

61+
public void SetIsExpanded(int index, bool isExpanded)
62+
=> ItemIsExpanded[index] = isExpanded;
63+
6164
public void InsertWithLevel(int index, object? item, int level)
6265
{
6366
if (level < 0) throw new ArgumentOutOfRangeException(nameof(level), level, "Item level must not be negative");
@@ -138,9 +141,7 @@ protected override void InsertItem(int index, object? item)
138141
}
139142

140143
protected override void MoveItem(int oldIndex, int newIndex)
141-
{
142-
MoveOffsetAdjustedItem(oldIndex, newIndex);
143-
}
144+
=> MoveOffsetAdjustedItem(oldIndex, newIndex);
144145

145146
internal void MoveOffsetAdjustedItem(int oldIndex, int newIndex)
146147
{
@@ -281,7 +282,7 @@ int GetAbsoluteIndex(int relativeIndex)
281282
}
282283
}
283284

284-
internal static class ListExtensions
285+
file static class ListExtensions
285286
{
286287
public static void MoveItem(this IList list, int oldIndex, int newIndex)
287288
{
@@ -295,7 +296,6 @@ public static void MoveItem(this IList list, int oldIndex, int newIndex)
295296

296297
public class MoveEventArgs : EventArgs
297298
{
298-
299299
public int OldIndex { get; }
300300
public int NewIndex { get; }
301301

MaterialDesignThemes.Wpf/TreeListView.cs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
using System.Collections.Specialized;
2+
using System.Windows.Automation.Peers;
3+
using MaterialDesignThemes.Wpf.Automation.Peers;
24
using MaterialDesignThemes.Wpf.Internal;
35

46
namespace MaterialDesignThemes.Wpf;
@@ -30,6 +32,9 @@ public TreeListView()
3032
{
3133
}
3234

35+
protected override AutomationPeer OnCreateAutomationPeer()
36+
=> new TreeListViewAutomationPeer(this);
37+
3338
private static object? CoerceItemsSource(DependencyObject d, object? baseValue)
3439
{
3540
if (d is TreeListView treeListView)
@@ -41,7 +46,7 @@ public TreeListView()
4146
}
4247

4348
protected override DependencyObject GetContainerForItemOverride()
44-
=> new TreeListViewItem();
49+
=> new TreeListViewItem(this);
4550

4651
protected override bool IsItemItsOwnContainerOverride(object? item)
4752
=> item is TreeListViewItem;
@@ -80,7 +85,9 @@ internal void ItemExpandedChanged(TreeListViewItem item)
8085
{
8186
int index = ItemContainerGenerator.IndexFromContainer(item);
8287
var children = item.GetChildren().ToList();
83-
if (item.IsExpanded)
88+
bool isExpanded = item.IsExpanded;
89+
itemsSource.SetIsExpanded(index, isExpanded);
90+
if (isExpanded)
8491
{
8592
int parentLevel = itemsSource.GetLevel(index);
8693
for (int i = 0; i < children.Count; i++)

0 commit comments

Comments
 (0)