<HierarchicalDataTemplate DataType="{x:Type local:GroupedBk}"
x:Key="TreeViewItemTemplate"
>
<GroupBox FontSize="50"
BorderBrush="Cyan"
BorderThickness="3"
Margin="5,50,5,50">
<GroupBox.Header>
<StackPanel Orientation="Horizontal">
<ToggleButton IsChecked="{Binding IsExpanded,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
FontSize="50"
Width="60">
<ToggleButton.Style>
<Style TargetType="ToggleButton">
<Setter Property="Content" Value="▼"/>
<Style.Triggers>
<Trigger Property="IsChecked" Value="False">
<Setter Property="Content" Value="▶"/>
</Trigger>
</Style.Triggers>
</Style>
</ToggleButton.Style>
</ToggleButton>
<TextBlock Text="{Binding GroupName}"
FontSize="50"
Margin="50,0,0,0"/>
</StackPanel>
</GroupBox.Header>
<ItemsControl ItemsSource="{Binding BksList}"
Visibility="{Binding IsExpanded,Converter={StaticResource BoolToVisibility}}"
ItemTemplate="{StaticResource BookRowDataTemplate}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</GroupBox>
</HierarchicalDataTemplate>
Install-Package Newtonsoft.json
//D:\C\WpfApp9\MainWindow.xaml
<Window x:Class="WpfApp9.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp9"
mc:Ignorable="d"
Title="{Binding MainTitle}"
WindowState="Maximized">
<Window.DataContext>
<local:MainVM/>
</Window.DataContext>
<Window.Resources>
<local:BoolToVisibility x:Key="BoolToVisibility"/>
<Style TargetType="TextBlock" x:Key="TbkStyle">
<Setter Property="FontSize" Value="30"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Foreground" Value="Red"/>
<Setter Property="FontWeight" Value="ExtraBold"/>
</Trigger>
</Style.Triggers>
</Style>
<DataTemplate DataType="{x:Type local:Book}"
x:Key="BookRowDataTemplate">
<Grid Margin="10"
Width="{x:Static SystemParameters.PrimaryScreenWidth}">
<Grid.Resources>
<Style TargetType="TextBlock" BasedOn="{StaticResource TbkStyle}"/>
</Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition MaxWidth="300"/>
<ColumnDefinition MaxWidth="300"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Id}" Grid.Row="0" Grid.Column="0"/>
<TextBlock Text="{Binding Name}" Grid.Row="0" Grid.Column="1"/>
<TextBlock Text="{Binding ISBN}" Grid.Row="0" Grid.Column="2"/>
<TextBlock Text="{Binding Comment}" Grid.Row="1" Grid.Column="0"/>
<TextBlock Text="{Binding Content}" Grid.Row="1" Grid.Column="1"/>
<TextBlock Text="{Binding Author}" Grid.Row="1" Grid.Column="2"/>
<TextBlock Text="{Binding Summary}" Grid.Row="2" Grid.Column="0"/>
<TextBlock Text="{Binding Title}" Grid.Row="2" Grid.Column="1"/>
<TextBlock Text="{Binding Topic}" Grid.Row="2" Grid.Column="2"/>
</Grid>
</DataTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:GroupedBk}"
x:Key="TreeViewItemTemplate">
<GroupBox FontSize="50"
BorderBrush="Cyan"
BorderThickness="3"
Margin="5,5,5,50">
<GroupBox.Header>
<StackPanel Orientation="Horizontal">
<ToggleButton IsChecked="{Binding IsExpanded,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
FontSize="50"
Width="60">
<ToggleButton.Style>
<Style TargetType="ToggleButton">
<Setter Property="Content" Value="▼"/>
<Style.Triggers>
<Trigger Property="IsChecked" Value="False">
<Setter Property="Content" Value="▶"/>
</Trigger>
</Style.Triggers>
</Style>
</ToggleButton.Style>
</ToggleButton>
<TextBlock Text="{Binding GroupName}"
FontSize="50"
Margin="50,0,0,0"/>
</StackPanel>
</GroupBox.Header>
<ItemsControl ItemsSource="{Binding BksList}"
Visibility="{Binding IsExpanded,Converter={StaticResource BoolToVisibility}}"
ItemTemplate="{StaticResource BookRowDataTemplate}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</GroupBox>
</HierarchicalDataTemplate>
<Style TargetType="TreeViewItem">
<Setter Property="IsExpanded" Value="True"/>
</Style>
</Window.Resources>
<Grid>
<TreeView ItemsSource="{Binding GroupedBksCollection}"
ItemTemplate="{StaticResource TreeViewItemTemplate}"/>
</Grid>
</Window>
//D:\C\WpfApp9\MainWindow.xaml.cs
using Newtonsoft.Json;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Globalization;
using System.Net.Http;
using System.Runtime.CompilerServices;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WpfApp9
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
public class MainVM : INotifyPropertyChanged
{
private static string url = "https://localhost:5001/api/books/getbooks/";
//private static string url = "https://localhost:5051/api/books/getbooks/";
private static HttpClient client = new HttpClient()
{
Timeout=TimeSpan.FromHours(1)
};
private bool isLoading = false;
public MainVM()
{
if(!DesignerProperties.GetIsInDesignMode(new DependencyObject()))
{
_ = LoadBooksAsync();
}
}
private async Task LoadBooksAsync(int cnt=20)
{
if(isLoading)
{
return;
}
isLoading = true;
try
{
MainTitle = $"{DateTime.Now},loading...";
string jsonStr = await client.GetStringAsync($"{url}{cnt}");
var bks = JsonConvert.DeserializeObject<List<Book>>(jsonStr);
if(bks!=null && bks.Any())
{
MainTitle = $"{DateTime.Now},loaded {bks.Count} items," +
$"First Id:{bks.FirstOrDefault()?.Id}," +
$"Last Id:{bks.LastOrDefault()?.Id}";
var tempGroups = bks.GroupBy(x => x.CategoryName);
if(tempGroups!=null && tempGroups.Any())
{
GroupedBksCollection = new ObservableCollection<GroupedBk>();
foreach(var g in tempGroups)
{
GroupedBksCollection.Add(new GroupedBk()
{
GroupName = g.Key,
BksList = g.ToList()
});
}
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex?.Message);
}
finally
{
isLoading = false;
}
}
private string mainTitle = $"{DateTime.Now},loading...";
public string MainTitle
{
get
{
return mainTitle;
}
set
{
if(value!=mainTitle)
{
mainTitle = value;
OnPropertyChanged();
}
}
}
private ObservableCollection<GroupedBk> groupedBksCollection;
public ObservableCollection<GroupedBk> GroupedBksCollection
{
get
{
return groupedBksCollection;
}
set
{
if (value != groupedBksCollection)
{
groupedBksCollection = value;
OnPropertyChanged();
}
}
}
public event PropertyChangedEventHandler? PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propName = "")
{
var handler = Volatile.Read(ref PropertyChanged);
if (handler == null)
{
return;
}
handler(this, new PropertyChangedEventArgs(propName));
}
}
public class GroupedBk: INotifyPropertyChanged
{
private bool isExpanded = true;
public bool IsExpanded
{
get
{
return isExpanded;
}
set
{
if(value!=isExpanded)
{
isExpanded = value;
OnPropertyChanged();
}
}
}
public string GroupName { get; set; }
public List<Book> BksList { get; set; }
public event PropertyChangedEventHandler? PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propName = "")
{
var handler = Volatile.Read(ref PropertyChanged);
if (handler == null)
{
return;
}
handler(this, new PropertyChangedEventArgs(propName));
}
}
public class Book
{
public long Id { get; set; }
public string Name { get; set; }
public string Author { get; set; }
public string CategoryName { get; set; }
public string ISBN { get; set; }
public string Comment { get; set; }
public string Content { get; set; }
public string Summary { get; set; }
public string Title { get; set; }
public string Topic { get; set; }
}
public class BoolToVisibility : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if(Boolean.TryParse(value?.ToString(),out bool isVisible))
{
if(isVisible)
{
return Visibility.Visible;
}
else
{
return Visibility.Collapsed;
}
}
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
![image]()
![image]()
![image]()
![image]()
![image]()