How to bind specific, deserialized from XML class structure to Treeview

后端 未结 1 2008
春和景丽
春和景丽 2021-01-24 10:57

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

1条回答
  •  走了就别回头了
    2021-01-24 11:45

    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:

    enter image description here

    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 TextBlocks, and wrap them all up in a container such as a Grid.

    Here is something that works:

    
        
            
            
        
        
            
                
                
            
            
                
                
            
            
                
                    
                        
                    
                    
                        
                        
                    
                    
                    
                
            
            
                
                    
                        
                            
                            
                            
                            
                        
                        
                            
                            
                        
                        
                        
                        
                        
                        
                        
                        
                        
                    
                
            
        
        
            
                
                    

    And the UI it produces looks like:

    enter image description here

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

    0 讨论(0)
提交回复
热议问题