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

NEW FEATURE: Multi-select TreeView #3333

Merged
merged 55 commits into from
Oct 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
bdaff94
WIP
Keboo Aug 31, 2023
7a9bb4e
Working on clicking for selection
Keboo Aug 31, 2023
c247195
Working tree view with multi-selection and indentation
Keboo Sep 7, 2023
f2ab06e
Remove un-used Template property
Keboo Sep 7, 2023
aa8b7e5
Removed un-used styles
Keboo Sep 7, 2023
4dd7594
More work on the multi-select tree view.
Keboo Sep 14, 2023
abdaf82
Fixed both know items bugs with the tree view
Keboo Sep 21, 2023
0c4c279
Handling double click
Keboo Sep 21, 2023
adfb02e
Add DebuggerDisplay to TreeItem
nicolaihenriksen Sep 23, 2023
9778826
Added failing UI test for issue 1
nicolaihenriksen Sep 23, 2023
d3f9808
Added failing UI test for issue 2
nicolaihenriksen Sep 23, 2023
27c6e2e
Fix for issue 1
nicolaihenriksen Sep 24, 2023
d4dcfa7
Fix for issue 2
nicolaihenriksen Sep 24, 2023
967ac7a
Minor cleanup in helper method
nicolaihenriksen Sep 25, 2023
32e7773
Refactoring the files to put TreeListView in its own resource dictionary
Keboo Sep 28, 2023
1de4cf6
Adding tests for TreeListViewItemsCollection
Keboo Sep 28, 2023
e145b29
Adding more UI tests for Move/Replace
Keboo Sep 28, 2023
5716b82
Adding test for reset of nested items
Keboo Sep 28, 2023
6428418
Minor clean up
Keboo Sep 28, 2023
119679b
Increase delays to make existing tests produce consistent results.
nicolaihenriksen Sep 29, 2023
0f33fc5
Add failing UI tests for the Move cases (with expanded children)
nicolaihenriksen Sep 29, 2023
e32b962
Add UI tests for replacing item with children - this works already!
nicolaihenriksen Sep 29, 2023
77e29e9
Add TODO for fixing the Move issue in TreeListView
nicolaihenriksen Sep 29, 2023
5f21294
Add failing unit tests for the "move with children" scenario
nicolaihenriksen Sep 29, 2023
52b0e32
Fix wrong order in expected child collection (tests still fail)
nicolaihenriksen Sep 29, 2023
cb83152
Fixed Insert() and Move() operation on root level items
nicolaihenriksen Sep 29, 2023
da69864
Fixed Remove() on root level items
nicolaihenriksen Sep 29, 2023
2826915
Rewrite Replace() unit test so it takes expanded children into account
nicolaihenriksen Sep 29, 2023
8b626b0
Fix Replace() of root level items
nicolaihenriksen Sep 29, 2023
28016d9
Fix Add/Remove/Replace of nested items
nicolaihenriksen Sep 29, 2023
3bea4e3
Fix Reset() of nested items
nicolaihenriksen Sep 29, 2023
35c900a
Partial fix of Move() of nested items
nicolaihenriksen Sep 29, 2023
0d5057c
Fixed Add/Remove/Replace of nested items (grand-children case)
nicolaihenriksen Sep 29, 2023
2c607ed
Fix Move() of nested items (with expanded children)
nicolaihenriksen Sep 30, 2023
2636965
Added failing UI tests for case where child item disconnects
nicolaihenriksen Sep 30, 2023
fb52f35
Add comment in code regarding alternative hack
nicolaihenriksen Sep 30, 2023
21c457d
Fix bug when collapsing item
nicolaihenriksen Oct 1, 2023
b87efd5
Workaround for Move() of items with expanded children
nicolaihenriksen Oct 1, 2023
ab694af
Hopefully fixed leaking event handlers
nicolaihenriksen Oct 3, 2023
ec14c65
"Fixing" move of item with expanded children
nicolaihenriksen Oct 4, 2023
f42ae04
Updating UI tests
nicolaihenriksen Oct 4, 2023
d715bec
"Fix" Move of root-level items
nicolaihenriksen Oct 4, 2023
b10e80e
Playing around
Keboo Oct 5, 2023
fbb056b
WIP
Keboo Oct 7, 2023
e62cb20
More work on getting the tests working
Keboo Oct 7, 2023
0ac7dc3
Fix up a few tests
Keboo Oct 11, 2023
d7b78be
Killing off TreeListViewItemContentPresenter
Keboo Oct 11, 2023
6a0eb70
Adding more tests
Keboo Oct 15, 2023
185c5a9
Fixing issue with IsExpanded not being preserved when moving down
Keboo Oct 15, 2023
9b7b1a1
Add failing UI test for the crash found while QA testing
nicolaihenriksen Oct 20, 2023
31e9288
Fixing issue with IsExpanded not being set to false on collapse
Keboo Oct 21, 2023
07d559f
Disable animation for "non transistion" states of ToggleButton
nicolaihenriksen Oct 21, 2023
c6c8ac1
Separating tree list view toggle button style from treeview
Keboo Oct 22, 2023
1fdce0e
Fixing issues with focus visual style on focus not being resolved
Keboo Oct 24, 2023
bc52078
Adding template selector support
Keboo Oct 24, 2023
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -241,3 +241,5 @@ $RECYCLE.BIN/

#React.JS
.module-cache

*.lutconfig
94 changes: 93 additions & 1 deletion MainDemo.Wpf/Domain/TreesViewModel.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using System.Collections.ObjectModel;
using System.Collections;
using System.Collections.ObjectModel;
using System.Windows.Documents;
using MaterialDesignThemes.Wpf;

namespace MaterialDesignDemo.Domain;
Expand Down Expand Up @@ -45,6 +47,19 @@ public class Planet
public double Velocity { get; set; }
}

public class TestItem
{
public TestItem? Parent { get; set; }
public string Name { get; }
public ObservableCollection<TestItem> Items { get; }

public TestItem(string name, IEnumerable<TestItem> items)
{
Name = name;
Items = new ObservableCollection<TestItem>(items);
}
}

public sealed class MovieCategory
{
public MovieCategory(string name, params Movie[] movies)
Expand All @@ -61,13 +76,26 @@ public MovieCategory(string name, params Movie[] movies)
public sealed class TreesViewModel : ViewModelBase
{
private object? _selectedItem;
private TestItem? _selectedTreeItem;

public ObservableCollection<TestItem> TreeItems { get; } = new();

public ObservableCollection<MovieCategory> MovieCategories { get; }

public AnotherCommandImplementation AddCommand { get; }

public AnotherCommandImplementation RemoveSelectedItemCommand { get; }

public AnotherCommandImplementation AddListTreeItemCommand { get; }

public AnotherCommandImplementation RemoveListTreeItemCommand { get; }

public TestItem? SelectedTreeItem
{
get => _selectedTreeItem;
set => SetProperty(ref _selectedTreeItem, value);
}

public object? SelectedItem
{
get => _selectedItem;
Expand All @@ -76,6 +104,68 @@ public object? SelectedItem

public TreesViewModel()
{
Random random = new();
for(int i = 0; i < 10; i++)
{
TreeItems.Add(CreateTestItem(random, 1));
}

static TestItem CreateTestItem(Random random, int depth)
{
int numberOfChildren = depth < 5 ? random.Next(0, 6) : 0;
var children = Enumerable.Range(0, numberOfChildren).Select(_ => CreateTestItem(random, depth + 1));
var rv = new TestItem(GenerateString(random.Next(4, 10)), children);
foreach(var child in rv.Items)
{
child.Parent = rv;
}
return rv;
}

AddListTreeItemCommand = new(_ =>
{
if (SelectedTreeItem is { } treeItem)
{
var newItem = CreateTestItem(random, 1);
newItem.Parent = treeItem;
treeItem.Items.Add(newItem);
}
else
{
TreeItems.Add(CreateTestItem(random, 1));
}
});

RemoveListTreeItemCommand = new(items =>
{
if (items is IEnumerable enumerable)
{
foreach(TestItem testItem in enumerable)
{
if (testItem.Parent is { } parent)
{
parent.Items.Remove(testItem);
}
else
{
TreeItems.Remove(testItem);
}
}
}
if (SelectedTreeItem is { } selectedItem)
{
if (selectedItem.Parent is { } parent)
{
parent.Items.Remove(selectedItem);
}
else
{
TreeItems.Remove(selectedItem);
}
SelectedTreeItem = null;
}
});

MovieCategories = new ObservableCollection<MovieCategory>
{
new MovieCategory("Action",
Expand All @@ -87,6 +177,8 @@ public TreesViewModel()
new Movie("EuroTrip", "Jeff Schaffer")
)
};
MovieCategories.Add(MovieCategories[0]);
MovieCategories.Add(MovieCategories[1]);

AddCommand = new AnotherCommandImplementation(
_ =>
Expand Down
1 change: 0 additions & 1 deletion MainDemo.Wpf/MainWindow.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
xmlns:system="clr-namespace:System;assembly=mscorlib"
Title="Material Design in XAML"
Width="1150"
MaxHeight="{Binding Source={x:Static SystemParameters.MaximizedPrimaryScreenHeight}}"
d:DataContext="{d:DesignInstance domain:MainWindowViewModel}"
AutomationProperties.Name="{Binding Title, RelativeSource={RelativeSource Self}}"
Icon="favicon.ico"
Expand Down
2 changes: 1 addition & 1 deletion MainDemo.Wpf/Properties/launchSettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"profiles": {
"Demo App": {
"commandName": "Project",
"commandLineArgs": "-p Home -t Inherit -f LeftToRight"
"commandLineArgs": "-p Trees -t Inherit -f LeftToRight"
}
}
}
56 changes: 47 additions & 9 deletions MainDemo.Wpf/Trees.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,12 @@

<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" MaxHeight="450" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Style="{StaticResource MaterialDesignHeadline6TextBlock}" Text="Basic Tree View:" />

<TextBlock Style="{StaticResource MaterialDesignHeadline6TextBlock}" Text="Basic Tree View:" />
<smtx:XamlDisplay Grid.Row="1"
Grid.Column="0"
VerticalContentAlignment="Top"
Expand Down Expand Up @@ -113,7 +113,6 @@
Margin="32,0,0,0"
Style="{StaticResource MaterialDesignHeadline6TextBlock}"
Text="MVVM/Binding:" />

<smtx:XamlDisplay Grid.Row="1"
Grid.Column="1"
Margin="32,0,0,0"
Expand Down Expand Up @@ -175,11 +174,52 @@
</Grid>
</smtx:XamlDisplay>

<TextBlock Style="{StaticResource MaterialDesignHeadline6TextBlock}" Text="Multi-Select Tree View:"
Grid.Column="2"/>
<smtx:XamlDisplay Grid.Row="1"
Grid.Column="2"
VerticalContentAlignment="Top"
UniqueKey="trees_3">
<Grid>
<materialDesign:TreeListView MinWidth="220" MaxHeight="450"
ItemsSource="{Binding TreeItems}"
SelectedItem="{Binding SelectedTreeItem}">
<materialDesign:TreeListView.Resources>
<HierarchicalDataTemplate DataType="{x:Type domain:TestItem}"
ItemsSource="{Binding Items, Mode=OneTime}">
<TextBlock Margin="3,2" Text="{Binding Name, Mode=OneTime}" />
</HierarchicalDataTemplate>

<HierarchicalDataTemplate DataType="{x:Type domain:MovieCategory}"
ItemsSource="{Binding Movies, Mode=OneTime}">
<TextBlock Margin="3,2" Text="{Binding Name, Mode=OneTime}" />
</HierarchicalDataTemplate>

<DataTemplate DataType="{x:Type domain:Movie}">
<TextBlock Margin="3,2"
Text="{Binding Name, Mode=OneTime}"
ToolTip="{Binding Director, Mode=OneTime}" />
</DataTemplate>
</materialDesign:TreeListView.Resources>

</materialDesign:TreeListView>
<StackPanel Orientation="Horizontal" VerticalAlignment="Bottom" HorizontalAlignment="Right">
<Button Command="{Binding AddListTreeItemCommand}"
ToolTip="Add an item"
Content="{materialDesign:PackIcon Kind=Add}"/>

<Button Command="{Binding RemoveListTreeItemCommand}"
ToolTip="Remove selected item(s)"
Content="{materialDesign:PackIcon Kind=Remove}"/>

</StackPanel>
</Grid>
</smtx:XamlDisplay>

<TextBlock Grid.Row="2"
Style="{StaticResource MaterialDesignHeadline6TextBlock}"
Text="Additional node content, syntax 1:" />

<smtx:XamlDisplay Grid.Row="3" UniqueKey="trees_3">
<smtx:XamlDisplay Grid.Row="3" UniqueKey="trees_4">
<TreeView>
<materialDesign:TreeViewAssist.AdditionalTemplate>
<DataTemplate>
Expand Down Expand Up @@ -217,11 +257,10 @@
Margin="32,0,0,0"
Style="{StaticResource MaterialDesignHeadline6TextBlock}"
Text="Additional node content, syntax 2:" />

<smtx:XamlDisplay Grid.Row="3"
Grid.Column="1"
Margin="32,0,0,0"
UniqueKey="trees_4">
UniqueKey="trees_5">
<TreeView>
<materialDesign:TreeViewAssist.AdditionalTemplateSelector>
<domain:TreeExampleSimpleTemplateSelector>
Expand Down Expand Up @@ -267,11 +306,10 @@
Margin="32,0,0,0"
Style="{StaticResource MaterialDesignHeadline6TextBlock}"
Text="Additional node content, showcase:" />

<smtx:XamlDisplay Grid.Row="3"
Grid.Column="2"
Margin="32,0,0,0"
UniqueKey="trees_5">
UniqueKey="trees_6">
<TreeView MinWidth="220" DisplayMemberPath="Name">
<TreeView.Resources>
<DataTemplate DataType="{x:Type domain:Planet}">
Expand Down
6 changes: 4 additions & 2 deletions MaterialDesignThemes.UITests/TestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@
[assembly: GenerateHelpers(typeof(ColorPicker))]
[assembly: GenerateHelpers(typeof(DialogHost))]
[assembly: GenerateHelpers(typeof(AutoSuggestBox))]
[assembly: GenerateHelpers(typeof(TreeListView))]
[assembly: GenerateHelpers(typeof(TreeListViewItem))]

namespace MaterialDesignThemes.UITests;

public abstract class TestBase : IAsyncLifetime
{
protected bool AttachedDebugger { get; set; } = true;
protected bool AttachedDebuggerToRemoteProcess { get; set; } = true;
protected ITestOutputHelper Output { get; }

[NotNull]
Expand Down Expand Up @@ -44,7 +46,7 @@ protected async Task<IVisualElement> LoadUserControl<TControl>()
public async Task InitializeAsync() =>
App = await XamlTest.App.StartRemote(new AppOptions
{
AllowVisualStudioDebuggerAttach = AttachedDebugger,
AllowVisualStudioDebuggerAttach = AttachedDebuggerToRemoteProcess,
LogMessage = Output.WriteLine
});
public async Task DisposeAsync() => await App.DisposeAsync();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<UserControl x:Class="MaterialDesignThemes.UITests.WPF.TreeListViews.TreeListViewDataBinding"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:MaterialDesignThemes.UITests.WPF.TreeListViews"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
mc:Ignorable="d"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<materialDesign:TreeListView
x:Name="TreeListView"
ItemsSource="{Binding Items}">
<materialDesign:TreeListView.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:TreeItem}"
ItemsSource="{Binding Children}">
<TextBlock Text="{Binding Value}" />
</HierarchicalDataTemplate>
</materialDesign:TreeListView.ItemTemplate>
</materialDesign:TreeListView>
<StackPanel Grid.Row="1" Orientation="Horizontal">
<Button Content="Add" Click="Add_OnClick" />
<Button Content="Remove" Click="Remove_OnClick" />
<Button Content="Replace" Click="Replace_OnClick" />
<Button Content="Down" Click="MoveDown_OnClick" />
<Button Content="Up" Click="MoveUp_OnClick" />
<Button Content="Reset" Click="Reset_OnClick" />
</StackPanel>
</Grid>
</UserControl>
Loading
Loading