C # XML Treeview (plumbing)

I am creating an XML viewer that needs to be able to read each XML file and put it in a tree structure. My goal is to create an XMLViewer control, the user should be able to modify certain routines in their own implementations. I provide standard implementations that offer basic functionality so that the XML viewer at least demonstrates the default behavior. I am trying to do this with plumbing and delegates.

What I have so far:

MainWindow.xaml

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="30*" />
        <RowDefinition Height="25*" />
        <RowDefinition Height="175*" />
    </Grid.RowDefinitions>
    <StackPanel Grid.Row="0" Orientation="Horizontal" Margin="0,5,0,5">
        <TextBlock Text="XML File" VerticalAlignment="Center" />
        <TextBox Name="txtPath" Width="400" IsReadOnly="True" Margin="5,0,5,0"/>
        <Button Content="Open" Name="btnOpen"  />
    </StackPanel>

    <Button Name="btnPlumb" Content="Plumb the code!" Grid.Row="1"/>
    <uc:XMLTreeView x:Name="XMLOutput" Grid.Row="2" />
</Grid>

      

MainWindow.xaml.cs

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        //Events
        btnOpen.Click += new RoutedEventHandler(ClickedOnOpen);
        btnPlumb.Click += new RoutedEventHandler(ClickedOnPlumb);
    }

    private void ClickedOnPlumb(object sender, RoutedEventArgs e)
    {
        plumbCode();
    }

    private void ClickedOnOpen(object sender, RoutedEventArgs e)
    {
        selectXMLFile();
    }

    private void selectXMLFile()
    {
        OpenFileDialog ofd = new OpenFileDialog();
        ofd.Filter = "XML-Files |*.xml";
        ofd.InitialDirectory = "C:\\";

        if (ofd.ShowDialog() == true)
        {
            string path = ofd.FileName;
            txtPath.Text = path;
            XMLOutput.processXML(path);
        }
    }

    private void plumbCode()
    {
        XMLOutput.PlumbTheCode();
    }
}

      

XMLTreeView Class

namespace XMLViewer
{
class XMLTreeView : TreeView
{
    public XmlDocument doc;

    public void processXML(string path)
    {
        XmlDocument document = new XmlDocument();
        this.doc = document;
        doc.Load(path);

        foreach (XmlNode node in doc.ChildNodes)
        {
            XMLTreeViewItem newItem = new XMLTreeViewItem(node);
            this.AddChild(newItem);
        }
    }

    public void PlumbTheCode()
    {
        this.Items.Clear();

        foreach (XmlNode node in doc.ChildNodes)
        {
            XMLTreeViewItem newItem;
            newItem = new XMLTreeViewItem(node);

            newItem._LoadColor = new LoadColorDelegate(newItem.LoadColorPlumbed);
            newItem._LoadColor.Invoke(node);


            this.AddChild(newItem);
        }



    }
}
}

      

XMLTreeViewItem Class

namespace XMLViewer
{

public delegate void LoadHeaderDelegate(XmlNode node);
public delegate void LoadColorDelegate(XmlNode node);
public delegate void CheckForChildrenDelegate(XmlNode node);

public class XMLTreeViewItem:TreeViewItem
{

   public LoadHeaderDelegate _LoadHeader { get; set; }
   public LoadColorDelegate _LoadColor { get; set; }
   public CheckForChildrenDelegate _CheckForChildren { get; set; }

    public XMLTreeViewItem(XmlNode node)
    {
        _LoadHeader = new LoadHeaderDelegate(LoadHeader);
        _LoadColor = new LoadColorDelegate(LoadColor);
        _CheckForChildren = new CheckForChildrenDelegate(CheckForChildren);

        _LoadHeader.Invoke(node);
        _LoadColor.Invoke(node);
        _CheckForChildren.Invoke(node);
    }

    #region HEADER
    private void LoadHeader(XmlNode RootNode)
    {
        if (RootNode.HasChildNodes == false)
        {
            this.Header = RootNode.InnerText.ToUpper();
        }
        else
        {
            this.Header = RootNode.Name.ToUpper();
        }

        if (RootNode.Attributes != null)
        {
            foreach (XmlAttribute attr in RootNode.Attributes)
            {
                this.Header += " " + attr.Name + " = " + attr.InnerText;
            }
        }
    } 
    #endregion

    #region COLOR
    private void LoadColor(XmlNode node)
    {
        this.Foreground = Brushes.Black;
    }
    public void LoadColorPlumbed(XmlNode node)
    {
        this.Foreground = Brushes.Green;
    } 
    #endregion

    #region CHILDREN
    private void CheckForChildren(XmlNode node)
    {
        if (node.HasChildNodes)
        {
            LoadChildren(node);
        }
    }

    private void LoadChildren(XmlNode RootNode)
    {
        foreach (XmlNode node in RootNode.ChildNodes)
        {
            XMLTreeViewItem newItem = new XMLTreeViewItem(node);
            this.AddChild(newItem);
        }
    } 
    #endregion
}
}

      

http://oi47.tinypic.com/34o94cw.jpg

My goal:

http://i46.tinypic.com/29uua83.png

As you can see, I am having trouble displaying the correct Treenodes settings. Anyone have an idea to fix this?

+3


source to share


1 answer


So you are trying to implement a tree view that has extension points for custom colorings / headers for xml nodes.

To do this, you've added a few public delegates for each XMLTreeViewItem that the caller can override to provide their own color / title / etc.

The problem with the current solution is that only the root node gets the custom delegate to paint. When the root xml node is constructed, all children are loaded by creating a new XMLTreeViewItem that has a standard implementation of LoadColor delegates.

You need to either copy the override delegation to every new node you create, or keep a reference to the root node that have overridden delegates.

Another problem is that the entire XMLTreeViewItem tree is generated in the constructor, and delegate overrides are provided only afterwards:

newItem = new XMLTreeViewItem(node);

newItem._LoadColor = new LoadColorDelegate(newItem.LoadColorPlumbed);
newItem._LoadColor.Invoke(node);

      

This means that at the time you do _LoadColor.Invoke, the whole tree is already built and initialized with its colors. _LoadColor = new LoadColorDelegate will overwrite the delegate you passed to all children and _LoadColor.Invoke will only be the color of the root node.

How do I suggest fixing this:



Move the LoadHeader / CheckForChildren / LoadColor methods (which you want to allow overrides) to the XMLTreeView class and expose them as public properties:

private void LoadColor(XMLTreeViewItem item, XmlNode node)
{
    item.Foreground = Brushes.Black;
}

      

Modify the XMLTreeViewItem constructor to accept an XMLTreeView instance and store the parent reference in each node:

public XMLTreeViewItem(XmlNode node, XMLTreeView parentTreeView)
{...}

      

Now let's create a tree like this:

public void PlumbTheCode()
{
    this.Items.Clear();

    this._LoadColor = new LoadColorDelegate(newItem.LoadColorPlumbed);
    foreach (XmlNode node in doc.ChildNodes)
    {
        this.AddChild(new XMLTreeViewItem(node, this));
    }
}

      

An alternative solution would be to pass the new coloring / title delegates directly to the XMLTreeViewItem constructor and pass recursively to all the bottom nodes. This will make the XMLTreeView easier for the coloring delegates, but it will probably make the customization more difficult for users of this class, since they will have to overwrite the delegate in every node.

0


source







All Articles