Search code examples
c#xmlwpftreeview

TreeView not filling from XML


I can't figure out why this isn't working.

Here's the relevant XAML:

<Window.Resources>
    <XmlDataProvider x:Key="Family" Source="TestArmy1.xml" XPath="/Army"/>
</Window.Resources>

// Snip

  <TreeView Name="OOB" Height="880" Background="#00000000" 
            BorderBrush="#00000000"  Padding="100,1,1,50" UseLayoutRounding="False"
            MouseRightButtonDown="TreeViewPreviewRightButtonDown" DataContext="{StaticResource Family}" ItemsSource="{Binding XPath=Commander}" FontSize="12" />

Here's the C# code:

        //This is for dynamically building a treeview with templates from an XML file
        XmlTextReader xmlReader1 = new XmlTextReader("HierarchicalDataTemplate1.xml");
        HierarchicalDataTemplate hierarchicalDataTemplate1 = XamlReader.Load(xmlReader1) as HierarchicalDataTemplate;

        XmlTextReader xmlReader2 = new XmlTextReader("HierarchicalDataTemplate2.xml");
        HierarchicalDataTemplate hierarchicalDataTemplate2 = XamlReader.Load(xmlReader2) as HierarchicalDataTemplate;
        hierarchicalDataTemplate1.ItemTemplate = hierarchicalDataTemplate2;

        XmlTextReader xmlReader3 = new XmlTextReader("HierarchicalDataTemplate3.xml");
        HierarchicalDataTemplate hierarchicalDataTemplate3 = XamlReader.Load(xmlReader3) as HierarchicalDataTemplate;
        hierarchicalDataTemplate2.ItemTemplate = hierarchicalDataTemplate3;

    OOB.ItemTemplate = hierarchicalDataTemplate1;

    Thread updateThread = new Thread(new ParameterizedThreadStart(UpdateTree));
    updateThread.Start(this);

And the code that builds the TreeView:

  private void UpdateTree(object obj)
    {
        if (File.Exists("TestArmy1.xml") == false)
        {
            MessageBox.Show("Unable to open\nTestArmy1.xml");
            return;
        }

        MainWindow window = (MainWindow)obj;
        window.Dispatcher.Invoke(DispatcherPriority.Send, new Action(RebuildTree));
    }

    private void RebuildTree()
    {
        XmlDataProvider provider = new XmlDataProvider();
        XmlDocument xmlFile = new XmlDocument();
        xmlFile.Load("TestArmy1.xml");
        provider.Document = xmlFile;
        provider.XPath = "/Army";

        OOB.DataContext = provider;

        XmlNode node = xmlFile.DocumentElement.SelectSingleNode("ArmyName");
        ArmyNameString = node.InnerText;
        ArmyNameTitle.Content = ArmyNameString;

    }

This is the (now stripped down) XML file, TestArmy1.xml, that is loaded:

<?xml version="1.0" encoding="UTF-8"?>
<Army>
    <ArmyName>The Army of Northern Virginia</ArmyName>
     <Commander> 
         <CommanderName>The Emperor With the Very Long Name!</CommanderName>
         <CommanderLeadership>94</CommanderLeadership> 
            <Division>
                <DivisionCommanderName>Major General William T. Sherman</DivisionCommanderName>
                <DivisionCommanderLeadership>78</DivisionCommanderLeadership>
                    <Unit>
                        <UnitName>'Chasseurs à Cheval Garde"</UnitName>
                        <UnitType>Cavalry</UnitType>
                        <UnitKStrength>3</UnitKStrength>
                        <UnitStrength>456</UnitStrength>
                        <UnitQuality>10</UnitQuality>
                        <UnitMorale>7</UnitMorale>
                        <UnitLeadership>7</UnitLeadership>
                        <UnitAmmunition>99</UnitAmmunition>
                    </Unit>
                </Division>
            <Division>
                <DivisionCommanderName>Marshal Ney</DivisionCommanderName>
                <DivisionCommanderLeadership>8</DivisionCommanderLeadership>
            </Division>
    </Commander>
</Army>

Here's HierarchicalDataTemplate1:

<HierarchicalDataTemplate 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    ItemsSource="{Binding XPath=Commander}"
    >
  
  <Grid Height="62" Width="auto">
        <Grid Height="61" HorizontalAlignment="Left" Margin="0,0,0,0" Name="grid1" VerticalAlignment="Top" Width="auto">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="auto" />
                <ColumnDefinition Width="auto" />
                <ColumnDefinition Width="auto" />
                <ColumnDefinition Width="auto" />
                 <ColumnDefinition Width="auto" />
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="25" />
                <RowDefinition Height="30" />
            </Grid.RowDefinitions>
            <Image Source= "HeadQuarters.png" Grid.Column="1" Grid.RowSpan="2" HorizontalAlignment="Center" Stretch="None" OpacityMask="White"></Image>
            <Label Content="{Binding XPath=CommanderName}" Height="54" HorizontalAlignment="Left" Name="label1" VerticalAlignment="Top" FontFamily="K22 Monastic" FontSize="36"  Margin="2,4,0,6" Grid.RowSpan="2" Grid.Column="2" />
            <Label Content="Leadership:"  HorizontalAlignment="Left" Margin="5,0,0,0" VerticalAlignment="Bottom"  Grid.Column="3" />
            <ProgressBar  HorizontalAlignment="Left" Height="20" Name="CommanderLeadershipProgressBar" VerticalAlignment="Top" Width="150" Grid.Column="3" Grid.Row="2" Margin="10,0,0,0" Minimum="1" Maximum="100" Value="{Binding XPath=CommanderLeadership}" />
        <TextBlock Text="{Binding ElementName=CommanderLeadershipProgressBar, Path=Value, StringFormat={}{0:0}%}" HorizontalAlignment="Center" VerticalAlignment="Top" Grid.Column="3" Grid.Row="2"  />
            <Button Content="Create Subordinate Unit" Height="35" Width="auto" HorizontalAlignment="Left" Margin="10,0,50,0" Name="button1" VerticalAlignment="Center"  Grid.Column="4" Grid.RowSpan="2" />

        </Grid>
</Grid>

And here is HierarchialDataTemplate2:

<HierarchicalDataTemplate 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    ItemsSource="{Binding XPath=Commander}"
    >
  
  <Grid Height="62" Width="auto">
        <Grid Height="61" HorizontalAlignment="Left" Margin="0,0,0,0" Name="grid1" VerticalAlignment="Top" Width="auto">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="auto" />
                <ColumnDefinition Width="auto" />
                <ColumnDefinition Width="auto" />
                <ColumnDefinition Width="auto" />
                 <ColumnDefinition Width="auto" />
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="25" />
                <RowDefinition Height="30" />
            </Grid.RowDefinitions>
            <Image Source= "HeadQuarters.png" Grid.Column="1" Grid.RowSpan="2" HorizontalAlignment="Center" Stretch="None" OpacityMask="White"></Image>
            <Label Content="{Binding XPath=CommanderName}" Height="54" HorizontalAlignment="Left" Name="label1" VerticalAlignment="Top" FontFamily="K22 Monastic" FontSize="36"  Margin="2,4,0,6" Grid.RowSpan="2" Grid.Column="2" />
            <Label Content="Leadership:"  HorizontalAlignment="Left" Margin="5,0,0,0" VerticalAlignment="Bottom"  Grid.Column="3" />
            <ProgressBar  HorizontalAlignment="Left" Height="20" Name="CommanderLeadershipProgressBar" VerticalAlignment="Top" Width="150" Grid.Column="3" Grid.Row="2" Margin="10,0,0,0" Minimum="1" Maximum="100" Value="{Binding XPath=CommanderLeadership}" />
        <TextBlock Text="{Binding ElementName=CommanderLeadershipProgressBar, Path=Value, StringFormat={}{0:0}%}" HorizontalAlignment="Center" VerticalAlignment="Top" Grid.Column="3" Grid.Row="2"  />
            <Button Content="Create Subordinate Unit" Height="35" Width="auto" HorizontalAlignment="Left" Margin="10,0,50,0" Name="button1" VerticalAlignment="Center"  Grid.Column="4" Grid.RowSpan="2" />

        </Grid>
</Grid>
  

And here is HierarchicalDataTemplate3:

<HierarchicalDataTemplate 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    ItemsSource="{Binding XPath=Unit}"
    >
  
  <Grid Height="62" Width="auto">
        <Grid Height="61" HorizontalAlignment="Left" Margin="0,0,0,0" Name="grid1" VerticalAlignment="Top" Width="auto">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="auto" />
                <ColumnDefinition Width="auto" />
                <ColumnDefinition Width="auto" />
                <ColumnDefinition Width="auto" />
                 <ColumnDefinition Width="auto" />
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="25" />
                <RowDefinition Height="30" />
            </Grid.RowDefinitions>
        
            <Label Content="{Binding XPath=UnitName}" Height="54" HorizontalAlignment="Left" Name="label4" VerticalAlignment="Top" FontFamily="K22 Monastic" FontSize="36"  Margin="2,4,0,6" Grid.RowSpan="2" Grid.Column="2" />
          
        </Grid>
</Grid>

And this is the (new) output:

enter image description here

Which is fine as far as it goes (if anybody is interested, I'm building what's called an Order of Battle Table for a wargame). But it doesn't continue building the entire TreeView. I think it's just reading the first node and stopping.

What am I missing?


Solution

  • If I understand your problem and question right, it is that the sub nodes to Commander which could be Division do not render?

    In that case you should set the ItemsSource of the HierarchicalDataSource to "Division" instead of Commander:

    <HierarchicalDataTemplate xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" ItemsSource="{Binding XPath=Division}">
    

    You could then include a Data template for the Division node inline as shown below or define it elsewhere:

          <HierarchicalDataTemplate.ItemTemplate>
        <DataTemplate>
          <TextBlock Text="{Binding XPath=DivisionCommanderName}" />
        </DataTemplate>
      </HierarchicalDataTemplate.ItemTemplate>
    

    This template could be a HierarchicalDataTemplate it self, if you have more subnodes.

    Edit:

    In the C#-code there may be a misunderstanding in the hierarchy of templates:

      //This is for dynamically building a treeview with templates from an XML file
      XmlTextReader xmlReader1 = new XmlTextReader(@"E:\Temp\HierarchicalDataTemplate1.xml");
      HierarchicalDataTemplate hierarchicalDataTemplate1 = XamlReader.Load(xmlReader1) as HierarchicalDataTemplate;
    
      XmlTextReader xmlReader2 = new XmlTextReader("HierarchicalDataTemplate2.xml");
      HierarchicalDataTemplate hierarchicalDataTemplate2 = XamlReader.Load(xmlReader2) as HierarchicalDataTemplate;
      // Original: hierarchicalDataTemplate2.ItemTemplate = hierarchicalDataTemplate2;
      hierarchicalDataTemplate1.ItemTemplate = hierarchicalDataTemplate2;
    
      XmlTextReader xmlReader3 = new XmlTextReader("HierarchicalDataTemplate3.xml");
      HierarchicalDataTemplate hierarchicalDataTemplate3 = XamlReader.Load(xmlReader3) as HierarchicalDataTemplate;
      // Oridignal: hierarchicalDataTemplate3.ItemTemplate = hierarchicalDataTemplate3;
      hierarchicalDataTemplate2.ItemTemplate = hierarchicalDataTemplate3;
    

    And further: the templates must reflect the nodes of their corresponding level.