Skip to content

Add bindable DP TreeListView.SelectedItems #3849

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

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
Open
2 changes: 1 addition & 1 deletion src/MainDemo.Wpf/App.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
If you would prefer to use your own colors, there is an option for that as well:
PrimaryColor and SecondaryColor also support the constant string "Inherit" to specify the color should use the system theme accent color
-->
<materialDesign:CustomColorTheme BaseTheme="Light" PrimaryColor="Aqua" SecondaryColor="#FF006400" />
<!--<materialDesign:CustomColorTheme BaseTheme="Light" PrimaryColor="Aqua" SecondaryColor="#FF006400" />-->

<!-- You can also use the built-in theme dictionaries: -->
<!--
Expand Down
20 changes: 16 additions & 4 deletions src/MainDemo.Wpf/Domain/TreesViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,10 @@ public sealed class TreesViewModel : ViewModelBase

public AnotherCommandImplementation RemoveListTreeItemCommand { get; }

public AnotherCommandImplementation RemoveSelectedListTreeItemCommand { get; }

public ObservableCollection<TestItem?> SelectedTreeItems { get; } = [];

public TestItem? SelectedTreeItem
{
get => _selectedTreeItem;
Expand All @@ -119,7 +123,7 @@ public object? SelectedItem
public TreesViewModel()
{
Random random = new();
for(int i = 0; i < 10; i++)
for (int i = 0; i < 10; i++)
{
TreeItems.Add(CreateTestItem(random, 1));
}
Expand All @@ -128,8 +132,8 @@ 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)
var rv = new TestItem(GenerateString(random.Next(4, 10)), children);
foreach (var child in rv.Items)
{
child.Parent = rv;
}
Expand All @@ -154,7 +158,7 @@ static TestItem CreateTestItem(Random random, int depth)
{
if (items is IEnumerable enumerable)
{
foreach(TestItem testItem in enumerable)
foreach (TestItem testItem in enumerable)
{
if (testItem.Parent is { } parent)
{
Expand Down Expand Up @@ -226,6 +230,14 @@ static TestItem CreateTestItem(Random random, int depth)
}
},
_ => SelectedItem != null);

RemoveSelectedListTreeItemCommand = new(item =>
{
if (item is TestItem treeItem)
{
SelectedTreeItems.Remove(treeItem);
}
});
}

private static string GenerateString(int length)
Expand Down
122 changes: 77 additions & 45 deletions src/MainDemo.Wpf/Trees.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -209,53 +209,85 @@
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>
<StackPanel Orientation="Horizontal">
<Grid>
<materialDesign:TreeListView MinWidth="220"
MaxHeight="450"
ItemsSource="{Binding TreeItems}"
SelectedItem="{Binding SelectedTreeItem}"
SelectedItems="{Binding SelectedTreeItems}">
<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>

<!--
Because Data Virtualization is enabled on this tree view by default, if you don't bind the IsExpanded property to something in the bound view model,
you can loose the expanded state of items when the TreeListViewItem is recycled.

For more information:
https://github.com/MaterialDesignInXAML/MaterialDesignInXamlToolkit/issues/3640#issuecomment-2274086113
https://learn.microsoft.com/dotnet/desktop/wpf/advanced/optimizing-performance-controls?view=netframeworkdesktop-4.8&WT.mc_id=DT-MVP-5003472
-->
<materialDesign:TreeListView.ItemContainerStyle>
<Style TargetType="materialDesign:TreeListViewItem" BasedOn="{StaticResource {x:Type materialDesign:TreeListViewItem}}">
<Setter Property="IsExpanded" Value="{Binding IsExpanded}" />
</Style>
</materialDesign:TreeListView.ItemContainerStyle>
</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}"/>

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

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

<!--
Because Data Virtualization is enabled on this tree view by default, if you don't bind the IsExpanded property to something in the bound view model,
you can loose the expanded state of items when the TreeListViewItem is recycled.

For more information:
https://github.com/MaterialDesignInXAML/MaterialDesignInXamlToolkit/issues/3640#issuecomment-2274086113
https://learn.microsoft.com/dotnet/desktop/wpf/advanced/optimizing-performance-controls?view=netframeworkdesktop-4.8&WT.mc_id=DT-MVP-5003472
-->
<materialDesign:TreeListView.ItemContainerStyle>
<Style TargetType="materialDesign:TreeListViewItem" BasedOn="{StaticResource {x:Type materialDesign:TreeListViewItem}}">
<Setter Property="IsExpanded" Value="{Binding IsExpanded}" />
</Style>
</materialDesign:TreeListView.ItemContainerStyle>
</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>
<GroupBox Margin="4,0,0,0" Header="TreeListView.SelectedItems">
<ListBox ItemsSource="{Binding SelectedTreeItems}" HorizontalContentAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="auto" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
Text="{Binding Name}"
VerticalAlignment="Center" />
<Button Grid.Column="1"
Padding="4"
Width="{Binding Path=ActualHeight, RelativeSource={RelativeSource Mode=Self}}"
Command="{Binding DataContext.RemoveSelectedListTreeItemCommand, RelativeSource={RelativeSource AncestorType=ListBox}}"
CommandParameter="{Binding .}"
Content="{materialDesign:PackIcon Kind=Bin}"
Foreground="Red"
Style="{StaticResource MaterialDesignFlatButton}" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</GroupBox>

</StackPanel>

</smtx:XamlDisplay>

<TextBlock Grid.Row="2"
Expand Down
12 changes: 12 additions & 0 deletions src/MaterialDesign3.Demo.Wpf/Domain/TreesViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,10 @@ public sealed class TreesViewModel : ViewModelBase

public AnotherCommandImplementation RemoveListTreeItemCommand { get; }

public AnotherCommandImplementation RemoveSelectedListTreeItemCommand { get; }

public ObservableCollection<TestItem?> SelectedTreeItems { get; } = [];

public TestItem? SelectedTreeItem
{
get => _selectedTreeItem;
Expand Down Expand Up @@ -210,6 +214,14 @@ static TestItem CreateTestItem(Random random, int depth)
}
},
_ => SelectedItem != null);

RemoveSelectedListTreeItemCommand = new(item =>
{
if (item is TestItem treeItem)
{
SelectedTreeItems.Remove(treeItem);
}
});
}

private static string GenerateString(int length)
Expand Down
78 changes: 56 additions & 22 deletions src/MaterialDesign3.Demo.Wpf/Trees.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -175,35 +175,69 @@
</Grid>
</smtx:XamlDisplay>

<TextBlock Style="{StaticResource MaterialDesignHeadline6TextBlock}" Text="Multi-Select Tree View:"
Grid.Column="2"/>
<TextBlock Grid.Column="2"
Style="{StaticResource MaterialDesignHeadline6TextBlock}"
Text="Multi-Select Tree View:" />
<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.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type domain:TestItem}"
ItemsSource="{Binding Items, Mode=OneWay}">
<TextBlock Margin="3,2" Text="{Binding Name, Mode=OneWay}" />
</HierarchicalDataTemplate>
</materialDesign:TreeListView.ItemTemplate>
<StackPanel Orientation="Horizontal">
<Grid>
<materialDesign:TreeListView MinWidth="220"
MaxHeight="450"
ItemsSource="{Binding TreeItems}"
SelectedItem="{Binding SelectedTreeItem}"
SelectedItems="{Binding SelectedTreeItems}">
<materialDesign:TreeListView.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type domain:TestItem}" ItemsSource="{Binding Items, Mode=OneWay}">
<TextBlock Margin="3,2" Text="{Binding Name, Mode=OneWay}" />
</HierarchicalDataTemplate>
</materialDesign:TreeListView.ItemTemplate>

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

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

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

<GroupBox Margin="4,0,0,0" Header="TreeListView.SelectedItems">
<ListBox HorizontalContentAlignment="Stretch" ItemsSource="{Binding SelectedTreeItems}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="auto" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
VerticalAlignment="Center"
Text="{Binding Name}" />
<Button Grid.Column="1"
Width="{Binding Path=ActualHeight, RelativeSource={RelativeSource Mode=Self}}"
Padding="4"
Command="{Binding DataContext.RemoveSelectedListTreeItemCommand, RelativeSource={RelativeSource AncestorType=ListBox}}"
CommandParameter="{Binding .}"
Content="{materialDesign:PackIcon Kind=Bin}"
Foreground="Red"
Style="{StaticResource MaterialDesignFlatButton}" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</GroupBox>

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

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

<TextBlock Grid.Row="2"
Expand Down
Loading
Loading