I want to make app which deserialized data from my xml file to class structure. I prepared classes by \'Paste XML as classes\' tool, however everything is made on common fields
To display a hierarchy of classes in a WPF TreeView
, in XAML you need to define a HierarchicalDataTemplate corresponding to each type of class in your hierarchy that might have children, and a DataTemplate corresponding to each type of class in your hierarchy that will not have children. Each data template should define a single framework element (TextBlock
, for instance, or a container control such as a DockPanel
with any number of embedded framework elements as children) that will display the data of that type of class in the tree, with appropriate bindings.
First, auto-generate classes for your XML using xsd.exe. To simply display the data in the tree you do not need to implement INotifyPropertyChanged
or use an ObservableCollection
for children:
///
[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;
///
[System.Xml.Serialization.XmlElementAttribute("nagłówek", Form = System.Xml.Schema.XmlSchemaForm.Unqualified)]
public planNagłówek[] Items
{
get
{
return this.itemsField;
}
set
{
this.itemsField = value;
}
}
}
///
[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;
///
[System.Xml.Serialization.XmlElementAttribute("autorzy", Form = System.Xml.Schema.XmlSchemaForm.Unqualified)]
public planNagłówekAutorzy[] autorzy
{
get
{
return this.autorzyField;
}
set
{
this.autorzyField = value;
}
}
}
///
[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;
///
[System.Xml.Serialization.XmlElementAttribute(Form = System.Xml.Schema.XmlSchemaForm.Unqualified)]
public string nazwa
{
get
{
return this.nazwaField;
}
set
{
this.nazwaField = value;
}
}
///
[System.Xml.Serialization.XmlElementAttribute("autor", Form = System.Xml.Schema.XmlSchemaForm.Unqualified)]
public planNagłówekAutorzyAutor[] autor
{
get
{
return this.autorField;
}
set
{
this.autorField = value;
}
}
}
///
[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;
///
[System.Xml.Serialization.XmlElementAttribute(Form = System.Xml.Schema.XmlSchemaForm.Unqualified)]
public string numer
{
get
{
return this.numerField;
}
set
{
this.numerField = value;
}
}
///
[System.Xml.Serialization.XmlElementAttribute(Form = System.Xml.Schema.XmlSchemaForm.Unqualified)]
public string imię
{
get
{
return this.imięField;
}
set
{
this.imięField = value;
}
}
///
[System.Xml.Serialization.XmlElementAttribute(Form = System.Xml.Schema.XmlSchemaForm.Unqualified)]
public string nazwisko
{
get
{
return this.nazwiskoField;
}
set
{
this.nazwiskoField = value;
}
}
///
[System.Xml.Serialization.XmlAttributeAttribute()]
public string atr
{
get
{
return this.atrField;
}
set
{
this.atrField = value;
}
}
}
Next, define a user interface in XAML in which to display these classes, manually creating appropriate data templates for each level:
Finally, load the data programmatically, for instance on startup:
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
string xml = @"
Autorzy:
222
Rust
Snow
111
Ian
Nower
";
var plan = XmlSerializationHelper.LoadFromXML(xml);
var xml2 = plan.GetXml();
Debug.WriteLine(xml2); // For testing
var children = new List();
children.Add(plan);
treeView1.Items.Clear();
treeView1.ItemsSource = children;
}
}
This produces something that looks like the following:
You will want to replace each template with something more beautiful.
Honestly, after grinding all this out I now believe the WinForms tree may be easier to work with.
Update - Editing
Re-reading your question, I see your requirement is to allow the user to load, edit in tree, and save the XML. This is more complicated than just loading. Here are the steps:
First, add custom routed UI commands for loading and saving XML:
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));
}
Next, add the actual c# logic in your Window1
class for these actions:
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
private void ExecutedLoadXML(object sender, ExecutedRoutedEventArgs e)
{
string xml = @"
Autorzy:
222
Rust
Snow
111
Ian
Nower
";
var plan = XmlSerializationHelper.LoadFromXML(xml);
var children = new List();
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;
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());
}
}
}
As you can see I'm just loading from a hardcoded string, and saving by doing a debug writeline. You will want to replace these with the real logic.
Next, in XAML, add the commands defined above to
Then, in XAML add a ToolBarTray
and ToolBar
with buttons to load and save XML, and bind the buttons to the commands you added to the CommandBindings
above.
Finally, in XAML, for each DataTemplate
or HierarchicalDataTemplate
that contains a data field, replace the TextBlock
for that field with an appropriate framework element for editing. Add any labels as desired as additional TextBlock
s, and wrap them all up in a container such as a Grid
.
Here is something that works:
And the UI it produces looks like:
I'm not a UI designer so you'll want to play with this to get something more beautiful.
Update 2
GetXML()
extension method:
public static class XmlSerializationHelper
{
public static string GetXml(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(this T obj, bool omitNamespace)
{
XmlSerializer serializer = new XmlSerializer(obj.GetType());
return GetXml(obj, serializer, omitNamespace);
}
public static string GetXml(this T obj)
{
return GetXml(obj, false);
}
public static T LoadFromXML(this string xmlString)
{
return xmlString.LoadFromXML(new XmlSerializer(typeof(T)));
}
public static T LoadFromXML(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(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);
}
}