WPF embed DataTemplate in HierchicalDataTemplate of TreeView

 <DataTemplate DataType="{x:Type local:Book}" x:Key="BookRowDataTemplate">
     <Border BorderBrush="LightGray"
             BorderThickness="2"
             Margin="5">
         <Grid Width="{Binding Source={x:Static SystemParameters.FullPrimaryScreenWidth},Converter={StaticResource LenConverter},ConverterParameter=1}"
               Height="{Binding Source={x:Static SystemParameters.FullPrimaryScreenHeight},Converter={StaticResource LenConverter},ConverterParameter=4}">
             <Grid.Resources>
                 <Style TargetType="TextBlock" BasedOn="{StaticResource TbkStyle}"/>
             </Grid.Resources>
             <Grid.RowDefinitions>
                 <RowDefinition/>
                 <RowDefinition/>
                 <RowDefinition/>
                 <RowDefinition/>
             </Grid.RowDefinitions>
             <Grid.ColumnDefinitions>
                 <ColumnDefinition/>
                 <ColumnDefinition/>
             </Grid.ColumnDefinitions>
             <TextBlock Grid.Row="0" Grid.Column="0" Text="{Binding Id}"/>
             <TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Name}"/>
             <TextBlock Grid.Row="1" Grid.Column="0" Text="{Binding Comment}"/>
             <TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding Summary}"/>
             <TextBlock Grid.Row="2" Grid.Column="0" Text="{Binding Title}"/>
             <TextBlock Grid.Row="2" Grid.Column="1" Text="{Binding Topic}"/>
             <TextBlock Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="2" Text="{Binding ISBN}"/>
         </Grid>
     </Border>
 </DataTemplate>

 <HierarchicalDataTemplate DataType="{x:Type local:GroupedBk}"
                           ItemsSource="{Binding BksList}">
     <GroupBox BorderBrush="Cyan"
               BorderThickness="3"
               Header="{Binding GroupName}"
               FontSize="30"/>
     <HierarchicalDataTemplate.ItemTemplate>
         <DataTemplate>
             <ContentControl Content="{Binding}"
                                 ContentTemplate="{StaticResource BookRowDataTemplate}"/>
         </DataTemplate>
     </HierarchicalDataTemplate.ItemTemplate>
 </HierarchicalDataTemplate>

 

 

The critical part is 

 <HierarchicalDataTemplate.ItemTemplate>
     <DataTemplate>
         <ContentControl Content="{Binding}"
                             ContentTemplate="{StaticResource BookRowDataTemplate}"/>
     </DataTemplate>
 </HierarchicalDataTemplate.ItemTemplate>

 

 

WPF:

<Window x:Class="WpfApp1.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:WpfApp1"
        mc:Ignorable="d"
        Title="MainWindow" WindowState="Maximized">
    <Window.Resources>

        <local:LenConverter x:Key="LenConverter"/>

        <Style TargetType="TextBlock" x:Key="TbkStyle">
            <Setter Property="FontSize" Value="30"/>
            <Setter Property="FontWeight" Value="Normal"/>
            <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">
            <Border BorderBrush="LightGray"
                    BorderThickness="2"
                    Margin="5">
                <Grid Width="{Binding Source={x:Static SystemParameters.FullPrimaryScreenWidth},Converter={StaticResource LenConverter},ConverterParameter=1}"
                      Height="{Binding Source={x:Static SystemParameters.FullPrimaryScreenHeight},Converter={StaticResource LenConverter},ConverterParameter=4}">
                    <Grid.Resources>
                        <Style TargetType="TextBlock" BasedOn="{StaticResource TbkStyle}"/>
                    </Grid.Resources>
                    <Grid.RowDefinitions>
                        <RowDefinition/>
                        <RowDefinition/>
                        <RowDefinition/>
                        <RowDefinition/>
                    </Grid.RowDefinitions>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition/>
                        <ColumnDefinition/>
                    </Grid.ColumnDefinitions>
                    <TextBlock Grid.Row="0" Grid.Column="0" Text="{Binding Id}"/>
                    <TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Name}"/>
                    <TextBlock Grid.Row="1" Grid.Column="0" Text="{Binding Comment}"/>
                    <TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding Summary}"/>
                    <TextBlock Grid.Row="2" Grid.Column="0" Text="{Binding Title}"/>
                    <TextBlock Grid.Row="2" Grid.Column="1" Text="{Binding Topic}"/>
                    <TextBlock Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="2" Text="{Binding ISBN}"/>
                </Grid>
            </Border>
        </DataTemplate>

        <HierarchicalDataTemplate DataType="{x:Type local:GroupedBk}"
                                  ItemsSource="{Binding BksList}">
            <GroupBox BorderBrush="Cyan"
                      BorderThickness="3"
                      Header="{Binding GroupName}"
                      FontSize="30"/>
            <HierarchicalDataTemplate.ItemTemplate>
                <DataTemplate>
                    <ContentControl Content="{Binding}"
                                        ContentTemplate="{StaticResource BookRowDataTemplate}"/>
                </DataTemplate>
            </HierarchicalDataTemplate.ItemTemplate>
        </HierarchicalDataTemplate>

        <Style TargetType="TreeViewItem">
            <Setter Property="IsExpanded" Value="True"/>
        </Style>

    </Window.Resources>
    <Window.DataContext>
        <local:MainVM/>
    </Window.DataContext>
    <Grid>
        <TreeView ItemsSource="{Binding GroupedBooks}"/>
    </Grid>
</Window>


using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Globalization;
using System.IO;
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;
using System.Xml.Serialization;

namespace WpfApp1
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
    }

    public class MainVM : INotifyPropertyChanged
    {
        private static readonly HttpClient httpClient = new HttpClient()
        {
            Timeout = TimeSpan.FromHours(1)
        };

        private static string originUrl = "http://localhost:62686/BookService.svc/getbooks?cnt=";
        public MainVM()
        {
            if (!DesignerProperties.GetIsInDesignMode(new DependencyObject()))
            {
                _ = InitBooksAsync(10);
            }
        }

        private async Task InitBooksAsync(int cnt = 10000)
        {
            string url = $"{originUrl}{cnt}";
            string xmlStr = await httpClient.GetStringAsync(url);
            var bksList = ConvertXmlStrToBooksList(xmlStr);
            var groupedBks = bksList.GroupBy(b => b.CategoryName);
            GroupedBooks = new ObservableCollection<GroupedBk>();
            foreach (var g in groupedBks)
            {
                GroupedBooks.Add(new GroupedBk()
                {
                    GroupName = g.Key,
                    BksList = g.ToList()
                });
            }
        }

        private ObservableCollection<GroupedBk> groupedBooks;
        public ObservableCollection<GroupedBk> GroupedBooks
        {
            get
            {
                return groupedBooks;
            }
            set
            {
                if (value != groupedBooks)
                {
                    groupedBooks = value;
                    OnPropertyChanged(nameof(GroupedBooks));
                }
            }
        }

        private List<Book> ConvertXmlStrToBooksList(string xmlStr)
        {
            var xmlSerializer = new XmlSerializer(typeof(XmlBook));
            using (var reader = new StringReader(xmlStr))
            {
                var xmlBk = xmlSerializer.Deserialize(reader) as XmlBook;
                if (xmlBk != null)
                {
                    return xmlBk.BksList;
                }
            }
            return null;
        }

        public event PropertyChangedEventHandler? PropertyChanged;
        private void OnPropertyChanged([CallerMemberName] string propertyName = "")
        {
            var handler = Volatile.Read(ref PropertyChanged);
            if (handler == null)
            {
                return;
            }
            handler.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    public class LenConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (double.TryParse(value?.ToString(), out double d) && double.TryParse(parameter?.ToString(), out double d2) && d2 > 0)
            {
                return d / d2;
            }
            return value;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

    public class GroupedBk
    {
        public string GroupName { get; set; }
        public List<Book> BksList { get; set; }
    }

    [XmlRoot("ArrayOfBook", Namespace = "http://schemas.datacontract.org/2004/07/WcfService1")]
    public class XmlBook
    {
        [XmlElement(nameof(Book))]
        public List<Book> BksList { get; set; }
    }

    public class Book
    {
        public long Id { get; set; }
        public string Name { get; set; }
        public string CategoryName { get; set; }
        public string ISBN { get; set; }
        public string Comment { get; set; }
        public string Summary { get; set; }
        public string Title { get; set; }
        public string Topic { get; set; }
    }
}

 

 

//WCF

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;

namespace WcfService1
{
    // NOTE: You can use the "Rename" command on the "Refactor" menu to change the interface name "IBookService" in both code and config file together.
    [ServiceContract]
    public interface IBookService
    {
        [OperationContract]
        [WebGet(UriTemplate="/getbooks?cnt={cnt}")]
        List<Book> GetBooks(int cnt = 10000);
    }

    enum BookCategory
    {
        Science,
        Technology,
        Engineering,
        Math
    }

    public class Book
    {
        public long Id { get; set; }
        public string Name { get; set; }
        public string CategoryName { get; set; }
        public string ISBN { get; set; }
        public string Comment { get; set; }
        public string Summary { get; set; }
        public string Title { get; set; }
        public string Topic { get; set; }
    }
}



using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using System.Threading;

namespace WcfService1
{
    // NOTE: You can use the "Rename" command on the "Refactor" menu to change the class name "BookService" in code, svc and config file together.
    // NOTE: In order to launch WCF Test Client for testing this service, please select BookService.svc or BookService.svc.cs at the Solution Explorer and start debugging.
    public class BookService : IBookService
    {
        private static long id = 1;
        private static string[] enumNames = Enum.GetNames(typeof(BookCategory));
        private static int enumsLen = enumNames.Length;
        private static Random rnd = new Random();

        private static (long, long) GetStartEnd(int cnt = 10000)
        {
            long end = Interlocked.Add(ref id, cnt);
            long start = end - cnt;
            return (start, end);
        }

        public List<Book> GetBooks(int cnt = 10000)
        {
            List<Book> bksList = new List<Book>(cnt);
            var (start, end) = GetStartEnd(cnt);
            for (long i = start; i < end; i++)
            {
                bksList.Add(new Book()
                {
                    Id = i,
                    Name = $"Name_{i}",
                    CategoryName = enumNames[rnd.Next(enumsLen)],
                    ISBN = $"ISBN_{i}_{Guid.NewGuid():N}",
                    Comment = $"Comment_{i}",
                    Summary = $"Summary_{i}",
                    Title = $"Title_{i}",
                    Topic = $"Topic_{i}"
                });
            }
            return bksList;
        }
    }
}


<?xml version="1.0"?>
<configuration>

  <appSettings>
    <add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />
  </appSettings>
  <system.web>
    <compilation debug="true" targetFramework="4.8" />
    <httpRuntime targetFramework="4.8"/>
  </system.web>
  <system.serviceModel>
      <bindings>
          <webHttpBinding>
              <binding name="BookServiceWebHttpBinding"
                       openTimeout="01:00:00"
                       closeTimeout="01:00:00"
                       sendTimeout="01:00:00"
                       receiveTimeout="01:00:00"
                       maxBufferPoolSize="2147483647"
                       maxBufferSize="2147483647"
                       maxReceivedMessageSize="2147483647">
                  <readerQuotas maxArrayLength="2147483647"
                                maxBytesPerRead="2147483647"
                                maxDepth="2147483647"
                                maxNameTableCharCount="2147483647"
                                maxStringContentLength="2147483647"/>
                  <security mode="None"/>
              </binding>
          </webHttpBinding>
      </bindings>
    <behaviors>
      <serviceBehaviors>
        <behavior name="BookServiceBehavior">
          <!-- To avoid disclosing metadata information, set the values below to false before deployment -->
          <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
          <!-- To receive exception details in faults for debugging purposes, set the value below to true.  Set to false before deployment to avoid disclosing exception information -->
          <serviceDebug includeExceptionDetailInFaults="false"/>
        </behavior>
      </serviceBehaviors>
        <endpointBehaviors>
            <behavior name="BookServiceEndpointBehavior">
                <webHttp/>
            </behavior>
        </endpointBehaviors>
    </behaviors>
    <protocolMapping>
        <add binding="basicHttpsBinding" scheme="https" />
    </protocolMapping>    
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
      <services>
          <service name="WcfService1.BookService"
                   behaviorConfiguration="BookServiceBehavior">
              <endpoint address=""
                        binding="webHttpBinding"
                        contract="WcfService1.IBookService"
                        behaviorConfiguration="BookServiceEndpointBehavior"
                        bindingConfiguration="BookServiceWebHttpBinding"/>
          </service>
      </services>
  </system.serviceModel>
  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true"/>
    <!--
        To browse web app root directory during debugging, set the value below to true.
        Set to false before deployment to avoid disclosing web app folder information.
      -->
    <directoryBrowse enabled="true"/>
  </system.webServer>

</configuration>

 

 

image

 

 

 

 

image

 

posted @ 2026-08-22 19:48  FredGrit  阅读(3)  评论(0)    收藏  举报