WPF TreeView explicitly given key name to HierarchicalDataTemplate

<Window.Resources>
    <Style TargetType="TreeViewItem">
        <Setter Property="IsExpanded" Value="False"/>
    </Style>

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

    <HierarchicalDataTemplate x:Key="DataTemplateA"
                              DataType="{x:Type local:Book}">
        <Border BorderBrush="LightBlue" 
                BorderThickness="2"
                Width="{Binding Source={x:Static SystemParameters.FullPrimaryScreenWidth},
                       Converter={StaticResource LenConverter},
                       ConverterParameter=2}"
                Height="{Binding Source={x:Static SystemParameters.FullPrimaryScreenHeight},
                       Converter={StaticResource LenConverter},
                       ConverterParameter=2}"
                Margin="5">
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition/>
                    <RowDefinition/>
                    <RowDefinition/>
                    <RowDefinition/>
                    <RowDefinition/>
                    <RowDefinition/>
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition/>
                    <ColumnDefinition/>
                </Grid.ColumnDefinitions>
                <Grid.Resources>
                    <Style TargetType="TextBlock">
                        <Setter Property="FontSize" Value="30"/>
                        <Style.Triggers>
                            <Trigger Property="IsMouseOver" Value="True">
                                <Setter Property="Foreground" Value="Red"/>
                            </Trigger>
                        </Style.Triggers>
                    </Style>
                </Grid.Resources>
                <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 Content}"/>
                <TextBlock Grid.Row="2" Grid.Column="0" Text="{Binding Author}"/>
                <TextBlock Grid.Row="2" Grid.Column="1" Text="{Binding CategoryName}"/>
                <TextBlock Grid.Row="3" Grid.Column="0" Text="{Binding Summary}"/>
                <TextBlock Grid.Row="3" Grid.Column="1" Text="{Binding Title}"/>
                <TextBlock Grid.Row="4" Grid.Column="0" Text="{Binding Topic}"/>
                <TextBlock Grid.Row="5" Grid.Column="0" Grid.ColumnSpan="2" Text="{Binding ISBN}"/>
            </Grid>
        </Border>
    </HierarchicalDataTemplate>

    <HierarchicalDataTemplate x:Key="DataTemplateB"
                              DataType="{x:Type local:Book}">
        <Border BorderBrush="Cyan" 
                BorderThickness="3"
                Margin="5" 
                Width="{Binding Source={x:Static SystemParameters.FullPrimaryScreenWidth},
                      Converter={StaticResource LenConverter},
                      ConverterParameter=2.2}"
                Height="{Binding Source={x:Static SystemParameters.FullPrimaryScreenHeight},
                      Converter={StaticResource LenConverter},
                      ConverterParameter=4}">
            <StackPanel>
                <StackPanel.Resources>
                    <Style TargetType="TextBlock">
                        <Setter Property="FontSize" Value="60"/>
                        <Style.Triggers>
                            <Trigger Property="IsMouseOver" Value="True">
                                <Setter Property="Foreground" Value="Red"/>
                            </Trigger>
                        </Style.Triggers>
                    </Style>
                </StackPanel.Resources>
                <TextBlock>Id:<Run Text="{Binding Id}"></Run></TextBlock>
                <TextBlock>ISBN:<Run Text="{Binding ISBN}"></Run></TextBlock>
            </StackPanel>
        </Border>
    </HierarchicalDataTemplate>

    <HierarchicalDataTemplate x:Key="TreeViewTemplateA"
                              ItemsSource="{Binding TreeNodesChildren}"
                              ItemTemplate="{StaticResource DataTemplateA}">
        <TextBlock FontSize="60" Text="{Binding NodeName}" Foreground="Green"/>
    </HierarchicalDataTemplate>

    <HierarchicalDataTemplate x:Key="TreeViewTemplateB"
                              ItemsSource="{Binding TreeNodesChildren}"
                              ItemTemplate="{StaticResource DataTemplateB}">
        <TextBlock Text="{Binding NodeName}" FontSize="100" Foreground="DarkBlue"/>
    </HierarchicalDataTemplate>
    
</Window.Resources>
<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition/>
        <ColumnDefinition/>
    </Grid.ColumnDefinitions>
    <TreeView Grid.Column="0" ItemsSource="{Binding TreeNodes}"
              ItemTemplate="{StaticResource TreeViewTemplateA}"/>
    <TreeView Grid.Column="1" ItemsSource="{Binding TreeNodes}"
       ItemTemplate="{StaticResource TreeViewTemplateB}"/>
</Grid>

 

<Window x:Class="WpfApp19.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:WpfApp19"
        mc:Ignorable="d"
        Title="{Binding MainTitle}"
        WindowState="Maximized">
    <Window.DataContext>
        <local:MainVM/>
    </Window.DataContext>
    
    <Window.Resources>
        <Style TargetType="TreeViewItem">
            <Setter Property="IsExpanded" Value="False"/>
        </Style>

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

        <HierarchicalDataTemplate x:Key="DataTemplateA"
                                  DataType="{x:Type local:Book}">
            <Border BorderBrush="LightBlue" 
                    BorderThickness="2"
                    Width="{Binding Source={x:Static SystemParameters.FullPrimaryScreenWidth},
                           Converter={StaticResource LenConverter},
                           ConverterParameter=2}"
                    Height="{Binding Source={x:Static SystemParameters.FullPrimaryScreenHeight},
                           Converter={StaticResource LenConverter},
                           ConverterParameter=2}"
                    Margin="5">
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition/>
                        <RowDefinition/>
                        <RowDefinition/>
                        <RowDefinition/>
                        <RowDefinition/>
                        <RowDefinition/>
                    </Grid.RowDefinitions>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition/>
                        <ColumnDefinition/>
                    </Grid.ColumnDefinitions>
                    <Grid.Resources>
                        <Style TargetType="TextBlock">
                            <Setter Property="FontSize" Value="30"/>
                            <Style.Triggers>
                                <Trigger Property="IsMouseOver" Value="True">
                                    <Setter Property="Foreground" Value="Red"/>
                                </Trigger>
                            </Style.Triggers>
                        </Style>
                    </Grid.Resources>
                    <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 Content}"/>
                    <TextBlock Grid.Row="2" Grid.Column="0" Text="{Binding Author}"/>
                    <TextBlock Grid.Row="2" Grid.Column="1" Text="{Binding CategoryName}"/>
                    <TextBlock Grid.Row="3" Grid.Column="0" Text="{Binding Summary}"/>
                    <TextBlock Grid.Row="3" Grid.Column="1" Text="{Binding Title}"/>
                    <TextBlock Grid.Row="4" Grid.Column="0" Text="{Binding Topic}"/>
                    <TextBlock Grid.Row="5" Grid.Column="0" Grid.ColumnSpan="2" Text="{Binding ISBN}"/>
                </Grid>
            </Border>
        </HierarchicalDataTemplate>

        <HierarchicalDataTemplate x:Key="DataTemplateB"
                                  DataType="{x:Type local:Book}">
            <Border BorderBrush="Cyan" 
                    BorderThickness="3"
                    Margin="5" 
                    Width="{Binding Source={x:Static SystemParameters.FullPrimaryScreenWidth},
                          Converter={StaticResource LenConverter},
                          ConverterParameter=2.2}"
                    Height="{Binding Source={x:Static SystemParameters.FullPrimaryScreenHeight},
                          Converter={StaticResource LenConverter},
                          ConverterParameter=4}">
                <StackPanel>
                    <StackPanel.Resources>
                        <Style TargetType="TextBlock">
                            <Setter Property="FontSize" Value="60"/>
                            <Style.Triggers>
                                <Trigger Property="IsMouseOver" Value="True">
                                    <Setter Property="Foreground" Value="Red"/>
                                </Trigger>
                            </Style.Triggers>
                        </Style>
                    </StackPanel.Resources>
                    <TextBlock>Id:<Run Text="{Binding Id}"></Run></TextBlock>
                    <TextBlock>ISBN:<Run Text="{Binding ISBN}"></Run></TextBlock>
                </StackPanel>
            </Border>
        </HierarchicalDataTemplate>

        <HierarchicalDataTemplate x:Key="TreeViewTemplateA"
                                  ItemsSource="{Binding TreeNodesChildren}"
                                  ItemTemplate="{StaticResource DataTemplateA}">
            <TextBlock FontSize="60" Text="{Binding NodeName}" Foreground="Green"/>
        </HierarchicalDataTemplate>

        <HierarchicalDataTemplate x:Key="TreeViewTemplateB"
                                  ItemsSource="{Binding TreeNodesChildren}"
                                  ItemTemplate="{StaticResource DataTemplateB}">
            <TextBlock Text="{Binding NodeName}" FontSize="100" Foreground="DarkBlue"/>
        </HierarchicalDataTemplate>
        
    </Window.Resources>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <TreeView Grid.Column="0" ItemsSource="{Binding TreeNodes}"
                  ItemTemplate="{StaticResource TreeViewTemplateA}"/>
        <TreeView Grid.Column="1" ItemsSource="{Binding TreeNodes}"
           ItemTemplate="{StaticResource TreeViewTemplateB}"/>
    </Grid>
</Window>



using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Globalization;
using System.Net.Http;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization;
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;

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

    public class MainVM : INotifyPropertyChanged
    {
        private HttpClient client;
        string originUrl = @"http://localhost:54941/BookService.svc/getbooks?cnt=";
        bool isLoading = false;
        public MainVM()
        {
            if (!DesignerProperties.GetIsInDesignMode(new DependencyObject()))
            {
                client = new HttpClient();
                _ = InitBooksAsync(1000);
            }
        }

        private async Task InitBooksAsync(int cnt = 1000000)
        {
            await Task.Run(async () =>
            {
                if (isLoading)
                {
                    return;
                }

                isLoading = true;
                try
                {
                    string xmlStr = await client.GetStringAsync($"{originUrl}{cnt}");
                    SetMainTitle($"Downloading from server finished");
                    if (!string.IsNullOrWhiteSpace(xmlStr))
                    {
                        XmlDocument xmlDoc = new XmlDocument();
                        xmlDoc.LoadXml(xmlStr);

                        await Application.Current?.Dispatcher?.InvokeAsync(() =>
                        {
                            var bks = DeserializeXmlDocument(xmlDoc);
                            TreeNodes = new ObservableCollection<TreeNodeItem>();
                            var groupedBooks = bks.GroupBy(x => x.CategoryName);

                            foreach(var group in groupedBooks)
                            {
                                TreeNodeItem treeNode = new TreeNodeItem()
                                {
                                    NodeName= group.Key,
                                    NodeValue=group.Key,
                                    TreeNodesChildren=new ObservableCollection<Book>(group.ToList())
                                };
                                TreeNodes.Add(treeNode);
                            }                             
                            
                            SetMainTitle($"FirstId:{bks.FirstOrDefault()?.Id},LastId:{bks?.LastOrDefault()?.Id}");
                            //Books = new ObservableCollection<Book>(XmlHelper.GetBooksFromXmlDocument(xmlDoc));
                        }, System.Windows.Threading.DispatcherPriority.Background);
                    }
                }
                catch (Exception ex)
                {
                    PrintMsg(ex?.Message);
                    isLoading = false;
                }
                finally
                {
                    isLoading = false;
                }
            });
        }

        private List<Book> DeserializeXmlDocument(XmlDocument xmlDoc)
        {
            List<Book> bksList = new List<Book>();
            try
            {
                using var xmlReader = new XmlNodeReader(xmlDoc);
                var serializer = new DataContractSerializer(typeof(List<Book>));
                bksList = (List<Book>)serializer.ReadObject(xmlReader);
                return bksList;
            }
            catch (Exception ex)
            {
                PrintMsg(ex?.Message);
                return null;
            }            
        }

        private void SetMainTitle(string msg)
        {
            Application.Current?.Dispatcher.InvokeAsync(() =>
            {
                MainTitle = $"{DateTime.Now.ToString("yyyyMMddHHmmssffff")},{msg}";
            }, System.Windows.Threading.DispatcherPriority.Background);
        }

        private void PrintMsg(string msg)
        {
#if DEBUG
            System.Diagnostics.Debug.WriteLine($"{DateTime.Now},{msg}");
#else
            System.Diagnostics.Trace.WriteLine($"{DateTime.Now},msg");
#endif
        }

        private ObservableCollection<TreeNodeItem> treeNodes;
        public ObservableCollection<TreeNodeItem> TreeNodes
        {
            get
            {
                return treeNodes;
            }
            set
            {
                if(value!=treeNodes)
                {
                    treeNodes = value;
                    OnPropertyChanged();
                }
            }
        }

        private string mainTitle = $"{DateTime.Now.ToString("yyyyMMddHHmmssffff")}";
        public string MainTitle
        {
            get
            {
                return mainTitle;
            }
            set
            {
                if (value != mainTitle)
                {
                    mainTitle = value;
                    OnPropertyChanged();
                }
            }
        }

        private ObservableCollection<Book> books;
        public ObservableCollection<Book> Books
        {
            get
            {
                return books;
            }
            set
            {
                if (value != books)
                {
                    books = value;
                    OnPropertyChanged();
                }
            }
        }

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

    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 p)
                && p>0.0d)
            {
                return d /p;
            }
            return value;
        }

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

    public class TreeNodeItem
    {
        public string NodeName { get; set; }
        public string NodeValue { get; set; }
        public ObservableCollection<Book> TreeNodesChildren { get; set; }

        public TreeNodeItem()
        {
            TreeNodesChildren = new ObservableCollection<Book>();            
        }
    }


    [DataContract(Namespace = "http://schemas.datacontract.org/2004/07/WcfService8")]
    public class Book
    {
        [DataMember]
        public long Id { get; set; }
        [DataMember]
        public string Name { get; set; }
        [DataMember]
        public string ISBN { get; set; }
        [DataMember]
        public string Comment { get; set; }
        [DataMember]
        public string Content { get; set; }
        [DataMember]
        public string Author { get; set; }
        [DataMember]
        public string CategoryName { get; set; }
        [DataMember]
        public string Summary { get; set; }
        [DataMember]
        public string Title { get; set; }
        [DataMember]
        public string Topic { get; set; }
    }
}

 

 

image

 

 

 

image

 

 

image

 

 

WCF Service generated data

//D:\C\WcfService8\WcfService8\IBookService.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;

namespace WcfService8
{
    // 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);
    }


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

    enum BookCategoryEnum
    {
        Science,
        Technology,
        Engineering,
        Math
    }
}


//D:\C\WcfService8\WcfService8\BookService.svc.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Syndication;
using System.Text;
using System.Threading;

namespace WcfService8
{
    // 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
    {
        static string[] enumNames = Enum.GetNames(typeof(BookCategoryEnum));
        static Random rnd = new Random();
        static int enumNamesCnt = enumNames.Length;

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

        public List<Book> GetBooks(int cnt)
        {
            var (start, end) = GetStartEnd((long)cnt);
            var bks = new List<Book>(cnt);
            for(long i= start; i < end; i++)
            {
                bks.Add(new Book()
                {
                    Id = i,
                    Name = $"Name_{i}",
                    ISBN = $"ISBN_{i}_{Guid.NewGuid():N}",
                    Comment = $"Comment_{i}",
                    Content = $"Content_{i}",
                    Summary = $"Summary_{i}",
                    Title = $"Title_{i}",
                    Topic = $"Topic_{i}",
                    Author = $"Author_{i}",
                    CategoryName = enumNames[rnd.Next(0,enumNamesCnt)]
                });
            }
            string msg = $"Start:{start},End:{end},interval:{cnt}";
            PrintMsg(msg);
            return bks;
        }

        private void PrintMsg(string msg)
        {
#if DEBUG
            System.Diagnostics.Debug.WriteLine($"{DateTime.Now},{msg}");
#else
            System.Diagnostics.Trace.WriteLine($"{DateTime.Now},msg");
#endif
        }
    }
}


//D:\C\WcfService8\WcfService8\Web.config
<?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>

      <services>
          <service name="WcfService8.BookService"
                   behaviorConfiguration="BookServiceBehavior">
              <endpoint address=""
                        binding="webHttpBinding"
                        contract="WcfService8.IBookService"
                        behaviorConfiguration="BookServiceEndPointBehavior"
                        bindingConfiguration="BookServiceWebHttpBinding"/>
          </service>
      </services>
    <protocolMapping>
        <add binding="basicHttpsBinding" scheme="https" />
    </protocolMapping>    
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
  </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>

 

posted @ 2026-07-18 20:21  FredGrit  阅读(4)  评论(0)    收藏  举报