0

I am trying to build a XML file editor, where I want to add XML file to treeview control, and based on it's nodes and attributes, i have to show editable textboxes or other controls. I serializing and deserializing this xml data to process in someother module.

I am trying to follow this Stackoverflow question but I cannot get it working, nothing binds with the control. I am fairly new to XAML and MVVM. so any help , suggestion will be really appreciated.

All I am getting is this

PS: Input is a xml file, so based on your suggestions i am open to bind xml directly to treeview or serialzed classes through MVVM[preffered].

XML File Structure:

    <?xml version="1.0" encoding="utf-8"?>
<interlocking xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <signals>
    <signal ref="SignalRef_1">
      <aspectSpeedDependencies>
        <aspectSpeedDependency aspect="REF" vApproach="VApproach" vPass="Vpas">
          <targetRef ref="TargetRef" />
          <targetRef ref="TargetRef" />
          <targetRef ref="TargetRef" />
        </aspectSpeedDependency>
        <aspectSpeedDependency aspect="REF" vApproach="VApproach" vPass="Vpas">
          <targetRef ref="TargetRef" />
          <targetRef ref="TargetRef" />
          <targetRef ref="TargetRef" />
        </aspectSpeedDependency>
        <aspectSpeedDependency aspect="REF" vApproach="VApproach" vPass="Vpas">
          <targetRef ref="TargetRef" />
          <targetRef ref="TargetRef" />
          <targetRef ref="TargetRef" />
        </aspectSpeedDependency>
      </aspectSpeedDependencies>
    </signal>
    <signal ref="SignalRef_1">
      <aspectSpeedDependencies>
        <aspectSpeedDependency aspect="REF" vApproach="VApproach" vPass="Vpas">
          <targetRef ref="TargetRef" />
          <targetRef ref="TargetRef" />
          <targetRef ref="TargetRef" />
        </aspectSpeedDependency>
        <aspectSpeedDependency aspect="REF" vApproach="VApproach" vPass="Vpas">
          <targetRef ref="TargetRef" />
          <targetRef ref="TargetRef" />
          <targetRef ref="TargetRef" />
        </aspectSpeedDependency>
        <aspectSpeedDependency aspect="REF" vApproach="VApproach" vPass="Vpas">
          <targetRef ref="TargetRef" />
          <targetRef ref="TargetRef" />
          <targetRef ref="TargetRef" />
        </aspectSpeedDependency>
      </aspectSpeedDependencies>
    </signal>
  </signals>
  <routes>
    <route id="1">
      <start>
        <signalRef ref="pro.Routes.Route.Start.SignalRef.Ref" />
      </start>
      <target>
        <signalRef ref="Target.SingalRef" />
      </target>
      <elements>
        <switchRef>
          <switch ref="Ref" course="Left" />
        </switchRef>
        <levelcrossingRef>
          <levelcrossing ref="Ref" beam="Beam" />
          <levelcrossing ref="Ref" beam="Beam" />
          <levelcrossing ref="Ref" beam="Beam" />
        </levelcrossingRef>
        <trainDetectorRef>
          <trackCircuitBorder ref="Ref" />
          <trackCircuitBorder ref="Ref" />
          <trackCircuitBorder ref="Ref" />
        </trainDetectorRef>
      </elements>
      <flankElements>Flank</flankElements>
      <routePriority rank="1" />
    </route>
  </routes>
</interlocking>

Code Behind

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
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.Shapes;
using System.Xml;
using System.Xml.Serialization;
//using CoreElements.Core.Interlocking;
using System.Xml.Linq;
using System.Reflection;

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


        private void ExecutedLoadXML(object sender, ExecutedRoutedEventArgs e)
        {

            string executableLocation = System.IO.Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
            string xslLocation = System.IO.Path.Combine(executableLocation, "Interlocking.xml");

            XDocument xmlData = XDocument.Load(xslLocation, LoadOptions.None);
            var Interlocking = XmlSerializationHelper.LoadFromXML<Interlocking>(xmlData.ToString());

            var children = new List<Interlocking>();
            children.Add(Interlocking);

            treeView1.ItemsSource = null;
            treeView1.Items.Clear();
            treeView1.ItemsSource = children;
        }

        private void ExecutedSaveXML(object sender, ExecutedRoutedEventArgs e)
        {
            var planList = treeView1.ItemsSource as IList<Interlocking>;
            if (planList != null && planList.Count > 0)
            {
                // Kludge to force pending edits to update
                treeView1.Focus();
                // Replace with actual save code!
                Debug.WriteLine(planList[0].GetXml());
            }
        }
    }

    public static class CustomCommands
    {
        public static readonly RoutedUICommand LoadXMLCommand = new RoutedUICommand("Load XML", "LoadXML", typeof(Window1));

        public static readonly RoutedUICommand SaveXMLCommand = new RoutedUICommand("Save XML", "SaveXML", typeof(Window1));
    }

    public static class XmlSerializationHelper
    {
        public static string GetXml<T>(T obj, XmlSerializer serializer, bool omitStandardNamespaces)
        {
            using (var textWriter = new StringWriter())
            {
                XmlWriterSettings settings = new XmlWriterSettings();
                settings.Indent = true;        // For cosmetic purposes.
                settings.IndentChars = "    "; // For cosmetic purposes.
                using (var xmlWriter = XmlWriter.Create(textWriter, settings))
                {
                    if (omitStandardNamespaces)
                    {
                        XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
                        ns.Add("", ""); // Disable the xmlns:xsi and xmlns:xsd lines.
                        serializer.Serialize(xmlWriter, obj, ns);
                    }
                    else
                    {
                        serializer.Serialize(xmlWriter, obj);
                    }
                }
                return textWriter.ToString();
            }
        }

        public static string GetXml<T>(this T obj, bool omitNamespace)
        {
            XmlSerializer serializer = new XmlSerializer(obj.GetType());
            return GetXml(obj, serializer, omitNamespace);
        }

        public static string GetXml<T>(this T obj)
        {
            return GetXml(obj, false);
        }

        public static T LoadFromXML<T>(this string xmlString)
        {
            return xmlString.LoadFromXML<T>(new XmlSerializer(typeof(T)));
        }

        public static T LoadFromXML<T>(this string xmlString, XmlSerializer serial)
        {
            T returnValue = default(T);

            using (StringReader reader = new StringReader(xmlString))
            {
                object result = serial.Deserialize(reader);
                if (result is T)
                {
                    returnValue = (T)result;
                }
            }
            return returnValue;
        }

        public static T LoadFromFile<T>(string filename)
        {
            XmlSerializer serial = new XmlSerializer(typeof(T));
            try
            {
                using (var fs = new FileStream(filename, FileMode.Open))
                {
                    object result = serial.Deserialize(fs);
                    if (result is T)
                    {
                        return (T)result;
                    }
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex.ToString());
                throw;
            }
            return default(T);
        }
    }
}

XAML:

 <Window x:Class="Test_Thesis.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:o="clr-namespace:Test_Thesis"
        mc:Ignorable="d"
      Title="Window1" Height="300" Width="600">
    <Window.CommandBindings>
        <CommandBinding Command="o:CustomCommands.LoadXMLCommand"  Executed="ExecutedLoadXML"/>
        <CommandBinding Command="o:CustomCommands.SaveXMLCommand" Executed="ExecutedSaveXML"/>
    </Window.CommandBindings>
    <Window.Resources>
        <HierarchicalDataTemplate  DataType="{x:Type o:Interlocking}" ItemsSource="{Binding Path=Signals}">
            <TextBlock Text="Interlocking">
            </TextBlock>
        </HierarchicalDataTemplate >
        <HierarchicalDataTemplate  DataType="{x:Type o:Signals}" ItemsSource="{Binding Path=Signal}">
            <TextBlock Text="Signal">
            </TextBlock>
        </HierarchicalDataTemplate >

        <!--<HierarchicalDataTemplate  DataType="{x:Type o:Signal}" ItemsSource="{Binding Path=AspectSpeedDependencies}">
            <TextBlock Text="Signals">
            </TextBlock>
        </HierarchicalDataTemplate >
        <HierarchicalDataTemplate  DataType="{x:Type o:AspectSpeedDependencies}" ItemsSource="{Binding Path=AspectSpeedDependency}">
            <TextBlock Text="AspectSpeedDependencies">
            </TextBlock>
        </HierarchicalDataTemplate >-->

        <HierarchicalDataTemplate  DataType="{x:Type o:Signal}" ItemsSource="{Binding Path=AspectSpeedDependencies}">
            <Grid Margin="3" MinWidth="300">
                <Grid.RowDefinitions>
                    <RowDefinition />
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition />
                    <ColumnDefinition />
                </Grid.ColumnDefinitions>
                <TextBlock Text="Ref" Grid.Column="0" Grid.Row="0"/>
                <TextBox Text="{Binding Path=Ref, Mode=TwoWay}" Grid.Column="1" Grid.Row="0"/>
            </Grid>
        </HierarchicalDataTemplate >
        <HierarchicalDataTemplate  DataType="{x:Type o:AspectSpeedDependencies}" ItemsSource="{Binding Path=AspectSpeedDependency}">
            <TextBlock Text="AspectSpeedDependencies">
            </TextBlock>
        </HierarchicalDataTemplate >

        <HierarchicalDataTemplate  DataType="{x:Type o:AspectSpeedDependency}" ItemsSource="{Binding Path=TargetRef}">

            <Border BorderBrush="Gray" BorderThickness="1" MinWidth="300">
                <StackPanel Height="auto" Width="auto">
                    <TextBlock Text="AspectSpeedDependency:" />

                        <StackPanel Orientation="Horizontal">

                            <TextBlock Text="Aspect:" Margin="1"/>
                        <TextBox Text="{Binding Path=Aspect, Mode=TwoWay}" Margin="1"/>
                        <TextBlock Text="VApproach:" Margin="1"/>
                        <TextBox Text="{Binding Path=VApproach, Mode=TwoWay}" Margin="1"/>
                        <TextBlock Text="VPass:" Margin="1"/>
                        <TextBox Text="{Binding Path=VPass, Mode=TwoWay}" Margin="1"/>
                    </StackPanel>



                        <!--<TextBlock Text="Aspect:" Grid.Column="0" Grid.Row="2"/>
                        <TextBox Text="{Binding Path=Aspect, Mode=TwoWay}" Grid.Column="1" Grid.Row="2"/>
                        <TextBlock Text="VApproach:" Grid.Column="0" Grid.Row="3"/>
                        <TextBox Text="{Binding Path=VApproach, Mode=TwoWay}" Grid.Column="1" Grid.Row="3"/>
                        <TextBlock Text="VPass:" Grid.Column="0" Grid.Row="4"/>
                        <TextBox Text="{Binding Path=VPass, Mode=TwoWay}" Grid.Column="1" Grid.Row="4"/>-->



                </StackPanel>

            </Border>
        </HierarchicalDataTemplate >

        <DataTemplate  DataType="{x:Type o:TargetRef}">
            <Border BorderBrush="Brown" BorderThickness="1" MinWidth="300">
                <Grid Margin="3" >
                    <Grid.RowDefinitions>
                        <RowDefinition />
                        <RowDefinition />
                        <RowDefinition />
                        <RowDefinition />
                    </Grid.RowDefinitions>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition />
                        <ColumnDefinition />
                    </Grid.ColumnDefinitions>
                    <TextBlock Text="Ref:" Grid.Column="0" Grid.Row="0"/>
                    <TextBox Text="{Binding Path=Ref, Mode=TwoWay}" Grid.Column="1" Grid.Row="0"/>



                </Grid>
            </Border>
        </DataTemplate >

    </Window.Resources>
    <DockPanel>
        <ToolBarTray DockPanel.Dock="Top">
            <ToolBar>
                <Button Command="o:CustomCommands.LoadXMLCommand" Content="{Binding RelativeSource={RelativeSource Self}, Path=Command.Text}"/>
                <Button Command="o:CustomCommands.SaveXMLCommand" Content="{Binding RelativeSource={RelativeSource Self}, Path=Command.Text}"/>
            </ToolBar>
        </ToolBarTray>
        <Grid DockPanel.Dock="Bottom">
            <TreeView Margin="3" Name="treeView1">
                <TreeView.ItemContainerStyle>
                    <Style TargetType="{x:Type TreeViewItem}">
                        <Setter Property="IsExpanded" Value="True" />
                    </Style>
                </TreeView.ItemContainerStyle>
            </TreeView>
        </Grid>
    </DockPanel>
</Window>

XML C# code

    using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Serialization;


namespace TreeviewTest.Stck
{


    [XmlRoot(ElementName = "targetRef")]
    public class TargetRef
    {
        [XmlAttribute(AttributeName = "ref")]
        public string Ref { get; set; }
    }

    [XmlRoot(ElementName = "aspectSpeedDependency")]
    public class AspectSpeedDependency
    {
        [XmlElement(ElementName = "targetRef")]
        public List<TargetRef> TargetRef { get; set; }
        [XmlAttribute(AttributeName = "aspect")]
        public string Aspect { get; set; }
        [XmlAttribute(AttributeName = "vApproach")]
        public string VApproach { get; set; }
        [XmlAttribute(AttributeName = "vPass")]
        public string VPass { get; set; }
    }

    [XmlRoot(ElementName = "aspectSpeedDependencies")]
    public class AspectSpeedDependencies
    {
        [XmlElement(ElementName = "aspectSpeedDependency")]
        public List<AspectSpeedDependency> AspectSpeedDependency { get; set; }
    }

    [XmlRoot(ElementName = "signal")]
    public class Signal
    {
        [XmlElement(ElementName = "aspectSpeedDependencies")]
        public List<AspectSpeedDependencies> AspectSpeedDependencies { get; set; }
        [XmlAttribute(AttributeName = "ref")]
        public string Ref { get; set; }
    }

    [XmlRoot(ElementName = "signals")]
    public class Signals
    {
        [XmlElement(ElementName = "signal")]
        public List<Signal> Signal { get; set; }
    }

    [XmlRoot(ElementName = "signalRef")]
    public class SignalRef
    {
        [XmlAttribute(AttributeName = "ref")]
        public string Ref { get; set; }
    }

    [XmlRoot(ElementName = "start")]
    public class Start
    {
        [XmlElement(ElementName = "signalRef")]
        public SignalRef SignalRef { get; set; }
    }

    [XmlRoot(ElementName = "target")]
    public class Target
    {
        [XmlElement(ElementName = "signalRef")]
        public SignalRef SignalRef { get; set; }
    }

    [XmlRoot(ElementName = "switch")]
    public class Switch
    {
        [XmlAttribute(AttributeName = "ref")]
        public string Ref { get; set; }
        [XmlAttribute(AttributeName = "course")]
        public string Course { get; set; }
    }

    [XmlRoot(ElementName = "switchRef")]
    public class SwitchRef
    {
        [XmlElement(ElementName = "switch")]
        public Switch Switch { get; set; }
    }

    [XmlRoot(ElementName = "levelcrossing")]
    public class Levelcrossing
    {
        [XmlAttribute(AttributeName = "ref")]
        public string Ref { get; set; }
        [XmlAttribute(AttributeName = "beam")]
        public string Beam { get; set; }
    }

    [XmlRoot(ElementName = "levelcrossingRef")]
    public class LevelcrossingRef
    {
        [XmlElement(ElementName = "levelcrossing")]
        public List<Levelcrossing> Levelcrossing { get; set; }
    }

    [XmlRoot(ElementName = "trackCircuitBorder")]
    public class TrackCircuitBorder
    {
        [XmlAttribute(AttributeName = "ref")]
        public string Ref { get; set; }
    }

    [XmlRoot(ElementName = "trainDetectorRef")]
    public class TrainDetectorRef
    {
        [XmlElement(ElementName = "trackCircuitBorder")]
        public List<TrackCircuitBorder> TrackCircuitBorder { get; set; }
    }

    [XmlRoot(ElementName = "elements")]
    public class Elements
    {
        [XmlElement(ElementName = "switchRef")]
        public SwitchRef SwitchRef { get; set; }
        [XmlElement(ElementName = "levelcrossingRef")]
        public LevelcrossingRef LevelcrossingRef { get; set; }
        [XmlElement(ElementName = "trainDetectorRef")]
        public TrainDetectorRef TrainDetectorRef { get; set; }
    }

    [XmlRoot(ElementName = "routePriority")]
    public class RoutePriority
    {
        [XmlAttribute(AttributeName = "rank")]
        public string Rank { get; set; }
    }

    [XmlRoot(ElementName = "route")]
    public class Route
    {
        [XmlElement(ElementName = "start")]
        public Start Start { get; set; }
        [XmlElement(ElementName = "target")]
        public Target Target { get; set; }
        [XmlElement(ElementName = "elements")]
        public Elements Elements { get; set; }
        [XmlElement(ElementName = "flankElements")]
        public string FlankElements { get; set; }
        [XmlElement(ElementName = "routePriority")]
        public RoutePriority RoutePriority { get; set; }
        [XmlAttribute(AttributeName = "id")]
        public string Id { get; set; }
    }

    [XmlRoot(ElementName = "routes")]
    public class Routes
    {
        [XmlElement(ElementName = "route")]
        public Route Route { get; set; }
    }

    [XmlRoot(ElementName = "interlocking")]
    public class Interlocking
    {
        [XmlElement(ElementName = "signals")]
        public List<Signals> Signals { get; set; }
        [XmlElement(ElementName = "routes")]
        public List<Routes> Routes { get; set; }
        [XmlAttribute(AttributeName = "xsi", Namespace = "http://www.w3.org/2000/xmlns/")]
        public string Xsi { get; set; }
        [XmlAttribute(AttributeName = "xsd", Namespace = "http://www.w3.org/2000/xmlns/")]
        public string Xsd { get; set; }
    }
 }

*********UPDATE*********** update

I am able to populate Signals, but I am not getting how would I will populate the second child , Routes now

*************Update***************

As per @J.H answer using CompositeCollection solved the issue. Save XML Command seems to be throwing exception but, XML is populated in Treeview now.

2
  • What's your question? What's not working? Commented Aug 25, 2016 at 14:27
  • @EdPlunkett Kindly read the updated question. XML data is not binding with treeview control. I am only getting this one static item in treeview Commented Aug 25, 2016 at 14:28

1 Answer 1

1

In class Interlocking, you have:

public Signals Signals { get; set; }

But it should be:

public List<Signals> Signals { get; set; }

Answer to your edit:

You can only have one item source so I suggest you combine the Signals and Routes into one collection. Change your Interlocking class to this:

[XmlRoot(ElementName = "interlocking")]
public class Interlocking
{
    [XmlElement(ElementName = "signals")]
    public List<Signals> Signals { get; set; }
    [XmlElement(ElementName = "routes")]
    public List<Routes> Routes { get; set; }
    [XmlAttribute(AttributeName = "xsi", Namespace = "http://www.w3.org/2000/xmlns/")]
    public string Xsi { get; set; }
    [XmlAttribute(AttributeName = "xsd", Namespace = "http://www.w3.org/2000/xmlns/")]
    public string Xsd { get; set; }

    public IList Children
    {
        get
        {
            return new CompositeCollection()
            {
                new CollectionContainer() { Collection = Signals },
                new CollectionContainer() { Collection = Routes }
            };
        }
    }
}

Notice the new property Children? It combines both Signals and Routes into one collection. You will then need to modify the XAML to use Children for it's ItemSource:

<HierarchicalDataTemplate DataType="{x:Type o:Interlocking}" ItemsSource="{Binding Path=Children}">
    <TextBlock Text="Interlocking">
    </TextBlock>
</HierarchicalDataTemplate >

And, of course, add the HierarchicalDataTemplate for the Routes type:

<HierarchicalDataTemplate  DataType="{x:Type o:Routes}" ItemsSource="{Binding Path=Route}">
    <TextBlock Text="Route">
    </TextBlock>
</HierarchicalDataTemplate >

FYI - this solution came from:

WPF Treeview Databinding Hierarchal Data with mixed types

Sign up to request clarification or add additional context in comments.

6 Comments

Thanks , I managed to get Routes in the Treeview, but "Routes" are still missing. I tried to make another HierarchicalDataTemplate but that doesn't yield anything. Do I have to make another treeview or can I still bind "Route" and "Signal" both in single Treeview? PS: I updated question with xaml and picture
I was thinking something on these lines, although i am not sure how these classes will turn into xmI when i will save the file. but I am getting Route as Children now. I will try to bind Route child according to this now. and update if something goes south. You saved me from a serious headache Sir :)
@Shahzad - put an XmlIgnore attribute on Children if needed. I'm happy to help, and save you a headache.
Finally all elements are in treeview :D getting problem saving back the editing values to xml but i am not sure if I should ask a new question or here.
You should probably ask a new question. If I get a minute, I'll look for the question but no promises... I'm in the middle of writing my own wpf app.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.