如何将特定的,反序列化的XML类结构绑定到Treeview

时间:2015-01-06 14:59:31

标签: c# xml wpf treeview

我想制作应用程序,将我的xml文件中的数据反序列化为类结构。我通过'粘贴XML作为类'工具准备了类,但是所有内容都是在公共字段或表上进行的,当我尝试为List或ObservableCollections进行更改时,serializator正确地停止加载xml文档。

我接下来要做的是从树视图中选择一些元素,编辑它,然后再次保存到xml文件。我不想直接在.xml上这样做。 这是我的XML示例:

    <plan xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="schema.xsd">
<nagłówek>
<autorzy>
<nazwa>Autorzy:</nazwa>
<autor atr="one">
<numer>222</numer>
<imię>Rust</imię>
<nazwisko>Snow</nazwisko>
</autor>

<autor>
<numer>111</numer>
<imię>Ian</imię>
<nazwisko>Nower</nazwisko>
</autor>
</autorzy>
</nagłówek>
...

以下是类

的例子
/// <remarks/>
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
[System.Xml.Serialization.XmlRootAttribute(Namespace = "", IsNullable = false)]
public partial class plan
{

    private planNagłówek nagłówekField;

    private planGłówny głównyField;

    /// <remarks/>
    public planNagłówek nagłówek
    {
        get
        {
            return this.nagłówekField;
        }
        set
        {
            this.nagłówekField = value;
        }
    }

    /// <remarks/>
    public planGłówny główny
    {
        get
        {
            return this.głównyField;
        }
        set
        {
            this.głównyField = value;
        }
    }
}

/// <remarks/>
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
public partial class planNagłówek 
{

    private planNagłówekAutorzy autorzyField;

    /// <remarks/>
    public planNagłówekAutorzy autorzy
    {
        get
        {
            return this.autorzyField;
        }
        set
        {
            this.autorzyField = value;
        }
    }
}

以及如何加载xml:

// Create an instance of the XmlSerializer specifying type and namespace.
            XmlSerializer serializer = new XmlSerializer(typeof(XML.plan));
            // A FileStream is needed to read the XML document.
            FileStream fs = new FileStream("...somepath.../Untitled4.xml", FileMode.Open);
            XmlReader reader = XmlReader.Create(fs);

            // Use the Deserialize method to restore the object's state.
            i = (XML.plan)serializer.Deserialize(reader);
            fs.Close();    

这是我得到的以及我的xml文件,也许这会帮助你帮助我:) https://drive.google.com/file/d/0B0wPodV30rnJSVA1ckVxWldDRDA/view

1 个答案:

答案 0 :(得分:1)

要在WPF TreeView中显示类的层次结构,在XAML中,您需要定义与层次结构中可能包含子项的每种类型相对应的HierarchicalDataTemplate,以及DataTemplate对应于层次结构中不具有子项的每种类型的类。每个数据模板应该定义一个框架元素(例如TextBlock或容器控件,例如DockPanel,其中包含任意数量的嵌入式框架元素作为子元素),它将显示该类型的数据树中的类,具有适当的绑定。

首先,使用xsd.exe为您的XML自动生成类。要简单地在树中显示数据,您不需要实施INotifyPropertyChanged或为儿童使用ObservableCollection<T>

/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "2.0.50727.3038")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
[System.Xml.Serialization.XmlRootAttribute(Namespace = "", IsNullable = false)]
public partial class plan
{

    private planNagłówek[] itemsField;

    /// <remarks/>
    [System.Xml.Serialization.XmlElementAttribute("nagłówek", Form = System.Xml.Schema.XmlSchemaForm.Unqualified)]
    public planNagłówek[] Items
    {
        get
        {
            return this.itemsField;
        }
        set
        {
            this.itemsField = value;
        }
    }
}

/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "2.0.50727.3038")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
public partial class planNagłówek
{

    private planNagłówekAutorzy[] autorzyField;

    /// <remarks/>
    [System.Xml.Serialization.XmlElementAttribute("autorzy", Form = System.Xml.Schema.XmlSchemaForm.Unqualified)]
    public planNagłówekAutorzy[] autorzy
    {
        get
        {
            return this.autorzyField;
        }
        set
        {
            this.autorzyField = value;
        }
    }
}

/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "2.0.50727.3038")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
public partial class planNagłówekAutorzy
{

    private string nazwaField;

    private planNagłówekAutorzyAutor[] autorField;

    /// <remarks/>
    [System.Xml.Serialization.XmlElementAttribute(Form = System.Xml.Schema.XmlSchemaForm.Unqualified)]
    public string nazwa
    {
        get
        {
            return this.nazwaField;
        }
        set
        {
            this.nazwaField = value;
        }
    }

    /// <remarks/>
    [System.Xml.Serialization.XmlElementAttribute("autor", Form = System.Xml.Schema.XmlSchemaForm.Unqualified)]
    public planNagłówekAutorzyAutor[] autor
    {
        get
        {
            return this.autorField;
        }
        set
        {
            this.autorField = value;
        }
    }
}

/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "2.0.50727.3038")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
public partial class planNagłówekAutorzyAutor
{

    private string numerField;

    private string imięField;

    private string nazwiskoField;

    private string atrField;

    /// <remarks/>
    [System.Xml.Serialization.XmlElementAttribute(Form = System.Xml.Schema.XmlSchemaForm.Unqualified)]
    public string numer
    {
        get
        {
            return this.numerField;
        }
        set
        {
            this.numerField = value;
        }
    }

    /// <remarks/>
    [System.Xml.Serialization.XmlElementAttribute(Form = System.Xml.Schema.XmlSchemaForm.Unqualified)]
    public string imię
    {
        get
        {
            return this.imięField;
        }
        set
        {
            this.imięField = value;
        }
    }

    /// <remarks/>
    [System.Xml.Serialization.XmlElementAttribute(Form = System.Xml.Schema.XmlSchemaForm.Unqualified)]
    public string nazwisko
    {
        get
        {
            return this.nazwiskoField;
        }
        set
        {
            this.nazwiskoField = value;
        }
    }

    /// <remarks/>
    [System.Xml.Serialization.XmlAttributeAttribute()]
    public string atr
    {
        get
        {
            return this.atrField;
        }
        set
        {
            this.atrField = value;
        }
    }
}

接下来,在XAML中定义一个用户界面,在其中显示这些类,为每个级别手动创建适当的数据模板:

<Window x:Class="WpfTreeViewNew.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:w="clr-namespace:WpfTreeViewNew"
    Title="Window1" Height="300" Width="600">
    <Window.Resources>
        <HierarchicalDataTemplate  DataType="{x:Type w:plan}" ItemsSource="{Binding Path=Items}">
            <TextBlock Text="Plan">
            </TextBlock>
        </HierarchicalDataTemplate >
        <HierarchicalDataTemplate  DataType="{x:Type w:planNagłówek}" ItemsSource="{Binding Path=autorzy}">
            <TextBlock Text="Nagłówek">
            </TextBlock>
        </HierarchicalDataTemplate >
        <HierarchicalDataTemplate  DataType="{x:Type w:planNagłówekAutorzy}" ItemsSource="{Binding Path=autor}">
            <TextBlock Text="{Binding Path=nazwa}"/>
        </HierarchicalDataTemplate >
        <DataTemplate  DataType="{x:Type w:planNagłówekAutorzyAutor}">
            <Border BorderBrush="Gray" BorderThickness="1" MinWidth="300">
                <StackPanel Orientation="Horizontal">
                    <TextBlock Margin="3" Text="{Binding Path=atr}"/>
                    <TextBlock Margin="3" Text="{Binding Path=numer}"/>
                    <TextBlock Margin="3" Text="{Binding Path=imię}"/>
                    <TextBlock Margin="3" Text="{Binding Path=nazwisko}"/>
                </StackPanel>
            </Border>
        </DataTemplate >
    </Window.Resources>
        <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>
</Window>

最后,以编程方式加载数据,例如在启动时:

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

        string xml = @"<plan xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xsi:noNamespaceSchemaLocation=""schema.xsd"">
  <nagłówek>
    <autorzy>
      <nazwa>Autorzy:</nazwa>
      <autor atr=""one"">
        <numer>222</numer>
        <imię>Rust</imię>
        <nazwisko>Snow</nazwisko>
      </autor>

      <autor>
        <numer>111</numer>
        <imię>Ian</imię>
        <nazwisko>Nower</nazwisko>
      </autor>
    </autorzy>
  </nagłówek>
</plan>
";
        var plan = XmlSerializationHelper.LoadFromXML<plan>(xml);
        var xml2 = plan.GetXml();
        Debug.WriteLine(xml2); // For testing

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

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

这会产生如下所示的内容:

enter image description here

您需要用更漂亮的东西替换每个模板。

老实说,在完成所有这些之后,我现在相信WinForms树may be easier to work with

更新 - 修改

重新阅读您的问题,我看到您的要求是允许用户加载在树中编辑,并保存 XML 。这比加载更复杂。以下是步骤:

首先,添加用于加载和保存XML的自定义路由UI命令:

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));
}

接下来,在Window1类中添加实际的c#逻辑以执行以下操作:

public partial class Window1 : Window
{
    public Window1()
    {
        InitializeComponent();
    }

    private void ExecutedLoadXML(object sender, ExecutedRoutedEventArgs e)
    {
        string xml = @"<plan xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xsi:noNamespaceSchemaLocation=""schema.xsd"">
  <nagłówek>
    <autorzy>
      <nazwa>Autorzy:</nazwa>
      <autor atr=""one"">
        <numer>222</numer>
        <imię>Rust</imię>
        <nazwisko>Snow</nazwisko>
      </autor>

      <autor>
        <numer>111</numer>
        <imię>Ian</imię>
        <nazwisko>Nower</nazwisko>
      </autor>
    </autorzy>
  </nagłówek>
</plan>
";
        var plan = XmlSerializationHelper.LoadFromXML<plan>(xml);

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

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

    private void ExecutedSaveXML(object sender, ExecutedRoutedEventArgs e)
    {
        var planList = treeView1.ItemsSource as IList<plan>;
        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());
        }
    }
}

正如您所看到的,我只是从硬编码字符串加载,并通过执行调试写入来保存。您将需要用真实逻辑替换它们。

接下来,在XAML中,将上面定义的命令添加到<Window.CommandBindings>

然后,在XAML中添加ToolBarTrayToolBar按钮以加载和保存XML,并将按钮绑定到您添加到上面CommandBindings的命令。

最后,在XAML中,对于包含数据字段的每个DataTemplateHierarchicalDataTemplate,将该字段的TextBlock替换为适当的框架元素进行编辑。根据需要添加任何标签作为附加TextBlock,并将它们全部包装在容器中,例如Grid

这是有用的:

<Window x:Class="WpfTreeViewNew.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:w="clr-namespace:WpfTreeViewNew"
    Title="Window1" Height="300" Width="600">
    <Window.CommandBindings>
        <CommandBinding Command="w:CustomCommands.LoadXMLCommand" Executed="ExecutedLoadXML"/>
        <CommandBinding Command="w:CustomCommands.SaveXMLCommand" Executed="ExecutedSaveXML"/>
    </Window.CommandBindings>
    <Window.Resources>
        <HierarchicalDataTemplate  DataType="{x:Type w:plan}" ItemsSource="{Binding Path=Items}">
            <TextBlock Text="Plan">
            </TextBlock>
        </HierarchicalDataTemplate >
        <HierarchicalDataTemplate  DataType="{x:Type w:planNagłówek}" ItemsSource="{Binding Path=autorzy}">
            <TextBlock Text="Nagłówek">
            </TextBlock>
        </HierarchicalDataTemplate >
        <HierarchicalDataTemplate  DataType="{x:Type w:planNagłówekAutorzy}" ItemsSource="{Binding Path=autor}">
            <Grid Margin="3" MinWidth="300">
                <Grid.RowDefinitions>
                    <RowDefinition />
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition />
                    <ColumnDefinition />
                </Grid.ColumnDefinitions>
                <TextBlock Text="nazwa:" Grid.Column="0" Grid.Row="0"/>
                <TextBox Text="{Binding Path=nazwa, Mode=TwoWay}" Grid.Column="1" Grid.Row="0"/>
            </Grid>
        </HierarchicalDataTemplate >
        <DataTemplate  DataType="{x:Type w:planNagłówekAutorzyAutor}">
            <Border BorderBrush="Gray" BorderThickness="1" MinWidth="300">
                <Grid Margin="3" >
                    <Grid.RowDefinitions>
                        <RowDefinition />
                        <RowDefinition />
                        <RowDefinition />
                        <RowDefinition />
                    </Grid.RowDefinitions>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition />
                        <ColumnDefinition />
                    </Grid.ColumnDefinitions>
                    <TextBlock Text="atr:" Grid.Column="0" Grid.Row="0"/>
                    <TextBox Text="{Binding Path=atr, Mode=TwoWay}" Grid.Column="1" Grid.Row="0"/>
                    <TextBlock Text="numer:" Grid.Column="0" Grid.Row="1"/>
                    <TextBox Text="{Binding Path=numer, Mode=TwoWay}" Grid.Column="1" Grid.Row="1"/>
                    <TextBlock Text="imię:" Grid.Column="0" Grid.Row="2"/>
                    <TextBox Text="{Binding Path=imię, Mode=TwoWay}" Grid.Column="1" Grid.Row="2"/>
                    <TextBlock Text="nazwisko:" Grid.Column="0" Grid.Row="3"/>
                    <TextBox Text="{Binding Path=nazwisko, Mode=TwoWay}" Grid.Column="1" Grid.Row="3"/>
                </Grid>
            </Border>
        </DataTemplate >
    </Window.Resources>
    <DockPanel>
        <ToolBarTray DockPanel.Dock="Top">
            <ToolBar>
                <Button Command="w:CustomCommands.LoadXMLCommand" Content="{Binding RelativeSource={RelativeSource Self}, Path=Command.Text}"/>
                <Button Command="w: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>

它产生的UI看起来像:

enter image description here

我不是UI设计师,所以你想要玩这个以获得更美丽的东西。

更新2

GetXML()扩展方法:

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);
    }
}