WPF HierarchicalDataTemplate customize via ToggleButton

 <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

 

posted @ 2026-09-05 18:53  FredGrit  阅读(6)  评论(0)    收藏  举报