• WPF快速实现XML可视化编辑工具


    虽然最近业余时间主要都放在研究AngularJS上了,不过由于正好要帮朋友做一个生成XML的小工具,顺便又温顾了一下WPF。虽然这个时代相对于Web应用和移动App,Windows应用程序是越来越少了,但是微软并未因此放弃它,反而推出了强大的WPF,让Windows应用程序的制作变得更优雅、更高效。 

    在我看来,WPF最大的强项就是布局和绑定了。WPF引入了MVVM的编程模式,并结合页面绑定,让UI和业务逻辑完全可以分离由不同的人去完成,而且只要View-Model保持稳定,对于View的布局变动将不受任何限制。因此WPF的编程思维和Winform已经完全不一样,如果你是一个从来没用过WPF的Winform程序员,你首要要做的应该是改变你的思维模式。

    案例需求

    需要一个工具,按照某机构的官方文档要求,数据由用户通过程序界面输入,最终生成指定格式的XML文件(某机构已提供XSLT文件,因此生成的XML可以在浏览器中展示出统一的格式。关于XSLT并不在本篇讨论范围内,以后有机会可以另外开篇再说)。

    Winform的思路

    1. 拖控件布局
    2. 创建控件的各种事件
    3. 通过后台代码控制界面布局以及元素行为
    4. 后台代码获取元素的值并将他们赋值给所需的对象
    5. 写非常复杂的if-else逻辑生成所需的XML
    6. 如果需要将生成的XML重新绑定到页面上,又是写一遍非常复杂的逻辑进行页面控件赋值

    WPF的思维

    1. 将XML抽象成实体类
    2. 创建实例并将它设置为View的DataContext
    3. 将实例的各个属性绑定到View的各个控件中
    4. 按需在界面上填写数据后,将实例序列化成XML
    5. 如果需要将生成的XML重新绑定到页面上,将文件内容反序列化为实例对象,绑定到DataContext即可

    光看文字描述,对于不熟悉WPF的人来说可能很难分清他们的区别,我们看下具体代码吧。为了简化业务,我重新写了一个Demo,通过页面上输入班级、老师、学生信息,生成一个包含班级信息的XML文件。

    步骤1:抽象XML实体对象

    为了能让实体对象的实例最终和View进行自动的双向绑定,我们需要将所有实体类实现INotifyPropertyChanged接口,为了进一步抽象代码,我们首先创建一个实现了INotifyPropertyChanged的基类,所有实体类将继承该基类。

    1     public abstract class ClassBase : INotifyPropertyChanged
    2     {
    3         public event PropertyChangedEventHandler PropertyChanged;
    4         protected void NotifyPropertyChange(string propertyName)
    5         {
    6             if (PropertyChanged != null)
    7                 PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    8         }
    9     }

    班级类(老师、学生属性使用ObservableCollection集合,可以使集合变动时界面也自动刷新布局):

     1     [XmlRoot(ElementName = "class")]
     2     public class MyClass : ClassBase
     3     {
     4         private string _grade;
     5         [XmlAttribute(AttributeName = "grade", Namespace = "")]
     6         public string Grade
     7         {
     8             get
     9             {
    10                 return _grade;
    11             }
    12             set
    13             {
    14                 _grade = value;
    15                 NotifyPropertyChange("Grade");
    16             }
    17         }
    18 
    19         private string _classID;
    20         [XmlAttribute(AttributeName = "class-id", Namespace = "")]
    21         public string ClassID
    22         {
    23             get
    24             {
    25                 return _classID;
    26             }
    27             set
    28             {
    29                 _classID = value;
    30                 NotifyPropertyChange("ClassID");
    31             }
    32         }
    33 
    34         private ObservableCollection<MyTeacher> _teachers;
    35         [XmlElement(ElementName = "teachers", Namespace = "")]
    36         public ObservableCollection<MyTeacher> Teachers
    37         {
    38             get
    39             {
    40                 return _teachers;
    41             }
    42             set
    43             {
    44                 _teachers = value;
    45                 NotifyPropertyChange("Teachers");
    46             }
    47         }
    48 
    49         private ObservableCollection<MyStudent> _students;
    50         [XmlElement(ElementName = "students", Namespace = "")]
    51         public ObservableCollection<MyStudent> Students
    52         {
    53             get
    54             {
    55                 return _students;
    56             }
    57             set
    58             {
    59                 _students = value;
    60                 NotifyPropertyChange("Students");
    61             }
    62         }
    63     }

    老师类:

     1     [XmlRoot(ElementName = "teacher")]
     2     public class MyTeacher : ClassBase
     3     {
     4         private string _name;
     5         [XmlElement(ElementName = "name", Namespace = "")]
     6         public string Name
     7         {
     8             get
     9             {
    10                 return _name;
    11             }
    12             set
    13             {
    14                 _name = value;
    15                 NotifyPropertyChange("Name");
    16             }
    17         }
    18 
    19         private string _teachingFor;
    20         [XmlElement(ElementName = "teaching-for", Namespace = "")]
    21         public string TeachingFor
    22         {
    23             get
    24             {
    25                 return _teachingFor;
    26             }
    27             set
    28             {
    29                 _teachingFor = value;
    30                 NotifyPropertyChange("TeachingFor");
    31             }
    32         }
    33 
    34         private string _comments;
    35         [XmlElement(ElementName = "comments", Namespace = "")]
    36         public string Comments
    37         {
    38             get
    39             {
    40                 return _comments;
    41             }
    42             set
    43             {
    44                 _comments = value;
    45                 NotifyPropertyChange("Comments");
    46             }
    47         }
    48     }

    学生类:

     1     [XmlRoot(ElementName = "student")]
     2     public class MyStudent : ClassBase
     3     {
     4         private string _name;
     5         [XmlElement(ElementName = "name", Namespace = "")]
     6         public string Name
     7         {
     8             get
     9             {
    10                 return _name;
    11             }
    12             set
    13             {
    14                 _name = value;
    15                 NotifyPropertyChange("Name");
    16             }
    17         }
    18 
    19         private int _age;
    20         [XmlElement(ElementName = "age", Namespace = "")]
    21         public int Age
    22         {
    23             get
    24             {
    25                 return _age;
    26             }
    27             set
    28             {
    29                 _age = value;
    30                 NotifyPropertyChange("Age");
    31             }
    32         }
    33 
    34         private string _gender;
    35         [XmlElement(ElementName = "gender", Namespace = "")]
    36         public string Gender
    37         {
    38             get
    39             {
    40                 return _gender;
    41             }
    42             set
    43             {
    44                 _gender = value;
    45                 NotifyPropertyChange("Gender");
    46             }
    47         }
    48     }

    OK,至此为止,我们Demo所需的XML实体类已抽象完毕。

    步骤2:创建实例并将它设置为View的DataContext

     1     public partial class MainWindow : Window
     2     {
     3         // 创建空实例
     4         private MyClass _myClassInfo = new MyClass();
     5 
     6         public MainWindow()
     7         {
     8             InitializeComponent();
     9 
    10             //将空实例设置为View的DataContext
    11             base.DataContext = _myClassInfo;
    12         }
    13     }

     对,你没看错,这一步就是如此简单!其实就注释的那2行代码而已!

    步骤3:将实例的各个属性绑定到View的各个空间中

    班级信息界面代码:

     1         <TextBlock Grid.Row="0" Grid.Column="0" Text="Grade:"></TextBlock>
     2         <ComboBox Grid.Row="0" Grid.Column="1" Text="{Binding Path=Grade}">
     3             <ComboBoxItem Content="Grade 1"></ComboBoxItem>
     4             <ComboBoxItem Content="Grade 2"></ComboBoxItem>
     5             <ComboBoxItem Content="Grade 3"></ComboBoxItem>
     6             <ComboBoxItem Content="Grade 4"></ComboBoxItem>
     7             <ComboBoxItem Content="Grade 5"></ComboBoxItem>
     8         </ComboBox>
     9 
    10         <TextBlock Grid.Row="1" Grid.Column="0" Text="ClassID:"></TextBlock>
    11         <TextBox Grid.Row="1" Grid.Column="1" Text="{Binding Path=ClassID}"></TextBox>

    老师信息界面代码:

     1     <GroupBox Header="Teachers" Grid.Row="2" Grid.ColumnSpan="2">
     2             <ContentControl>
     3                 <Grid>
     4                     <Grid.RowDefinitions>
     5                         <RowDefinition Height="*"></RowDefinition>
     6                         <RowDefinition Height="30"></RowDefinition>
     7                     </Grid.RowDefinitions>
     8                     
     9                     <TabControl x:Name="tabTeachers" ItemsSource="{Binding Path=Teachers}">
    10                         <TabControl.ItemTemplate>
    11                             <DataTemplate>
    12                                 <TextBlock Text="{Binding Path=Name, Converter={StaticResource TeacherNameConverter}}" MinWidth="30"></TextBlock>
    13                             </DataTemplate>
    14                         </TabControl.ItemTemplate>
    15                         <TabControl.ContentTemplate>
    16                             <DataTemplate>
    17                                 <Grid>
    18                                     <Grid.ColumnDefinitions>
    19                                         <ColumnDefinition Width="160"></ColumnDefinition>
    20                                         <ColumnDefinition Width="*"></ColumnDefinition>
    21                                     </Grid.ColumnDefinitions>
    22 
    23                                     <Grid.RowDefinitions>
    24                                         <RowDefinition Height="30"></RowDefinition>
    25                                         <RowDefinition Height="30"></RowDefinition>
    26                                         <RowDefinition Height="30"></RowDefinition>
    27                                     </Grid.RowDefinitions>
    28 
    29                                     <Label Grid.Row="0" Grid.Column="0" Content="Teacher name"></Label>
    30                                     <TextBox Grid.Row="0" Grid.Column="1" Text="{Binding Path=Name}"></TextBox>
    31 
    32                                     <Label Grid.Row="1" Grid.Column="0" Content="Teaching for"></Label>
    33                                     <TextBox Grid.Row="1" Grid.Column="1" Text="{Binding Path=TeachingFor}"></TextBox>
    34 
    35                                     <Label Grid.Row="2" Grid.Column="0" Content="Comments"></Label>
    36                                     <TextBox Grid.Row="2" Grid.Column="1" Text="{Binding Path=Comments}"></TextBox>
    37                                 </Grid>
    38                             </DataTemplate>
    39                         </TabControl.ContentTemplate>
    40                     </TabControl>
    41                     
    42                     <StackPanel Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Center">
    43                         <Button Name="btnNewTeacher" Content="Create New Teacher" Width="150" Margin="0,0,20,0" Click="btnNewTeacher_Click"></Button>
    44                         <Button Name="btnDeleteTeacher" Content="Delete Current Teacher" Width="150" Click="btnDeleteTeacher_Click"></Button>
    45                     </StackPanel>
    46                 </Grid>
    47             </ContentControl>
    48         </GroupBox>

    学生信息界面代码:

     1     <GroupBox Header="Students" Grid.Row="3" Grid.ColumnSpan="2">
     2             <ContentControl>
     3                 <Grid>
     4                     <Grid.RowDefinitions>
     5                         <RowDefinition Height="*"></RowDefinition>
     6                         <RowDefinition Height="30"></RowDefinition>
     7                     </Grid.RowDefinitions>
     8 
     9                     <TabControl x:Name="tabStudents" ItemsSource="{Binding Path=Students}">
    10                         <TabControl.ItemTemplate>
    11                             <DataTemplate>
    12                                 <TextBlock Text="{Binding Path=Name, Converter={StaticResource StudentNameConverter}}" MinWidth="30"></TextBlock>
    13                             </DataTemplate>
    14                         </TabControl.ItemTemplate>
    15                         <TabControl.ContentTemplate>
    16                             <DataTemplate>
    17                                 <Grid>
    18                                     <Grid.ColumnDefinitions>
    19                                         <ColumnDefinition Width="160"></ColumnDefinition>
    20                                         <ColumnDefinition Width="*"></ColumnDefinition>
    21                                     </Grid.ColumnDefinitions>
    22 
    23                                     <Grid.RowDefinitions>
    24                                         <RowDefinition Height="30"></RowDefinition>
    25                                         <RowDefinition Height="30"></RowDefinition>
    26                                         <RowDefinition Height="30"></RowDefinition>
    27                                     </Grid.RowDefinitions>
    28 
    29                                     <Label Grid.Row="0" Grid.Column="0" Content="Student name"></Label>
    30                                     <TextBox Grid.Row="0" Grid.Column="1" Text="{Binding Path=Name}"></TextBox>
    31 
    32                                     <Label Grid.Row="1" Grid.Column="0" Content="Age"></Label>
    33                                     <TextBox Grid.Row="1" Grid.Column="1" Text="{Binding Path=Age}"></TextBox>
    34 
    35                                     <Label Grid.Row="2" Grid.Column="0" Content="Gender"></Label>
    36                                     <ComboBox Grid.Row="2" Grid.Column="1" Text="{Binding Path=Gender}">
    37                                         <ComboBoxItem Content="Male"></ComboBoxItem>
    38                                         <ComboBoxItem Content="Female"></ComboBoxItem>
    39                                     </ComboBox>
    40                                 </Grid>
    41                             </DataTemplate>
    42                         </TabControl.ContentTemplate>
    43                     </TabControl>
    44 
    45                     <StackPanel Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Center">
    46                         <Button Name="btnNewStudent" Content="Create New Student" Width="150" Margin="0,0,20,0" Click="btnNewStudent_Click"></Button>
    47                         <Button Name="btnDeleteStudent" Content="Delete Current Student" Width="150" Click="btnDeleteStudent_Click"></Button>
    48                     </StackPanel>
    49                 </Grid>
    50             </ContentControl>
    51         </GroupBox>

    步骤4:按需在界面上填写数据后,将实例序列化成XML

     1 string xmlFilePath = txtFilePath.Text.Trim();
     2 if (!string.IsNullOrEmpty(xmlFilePath))
     3 {
     4     XmlWriterSettings settings = new XmlWriterSettings()
     5     {
     6         Encoding = Encoding.UTF8,
     7         OmitXmlDeclaration = true,
     8         NewLineOnAttributes = true,
     9         Indent = true,
    10         ConformanceLevel = ConformanceLevel.Document
    11     };
    12 
    13     XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
    14     ns.Add("", "");
    15 
    16     using (FileStream fs = new FileStream(xmlFilePath, FileMode.Create))
    17     using (var writer = XmlWriter.Create(fs, settings))
    18     {
    19         writer.WriteRaw("<?xml version="1.0" encoding="UTF-8"?>
    ");
    20 
    21         XmlSerializer xmlSerializer = new XmlSerializer(typeof(MyClass));
    22         xmlSerializer.Serialize(writer, _myClassInfo, ns);
    23         System.Windows.Forms.MessageBox.Show("Success!");
    24     }
    25 }
    26 else
    27 {
    28     System.Windows.Forms.MessageBox.Show("Choose a file path to save!");
    29 }

    步骤5:读取已有的XML绑定到页面上

     1 OpenFileDialog dialog = new OpenFileDialog();
     2 dialog.DefaultExt = "xml";
     3 dialog.Filter = "XML documents (*.xml)|*.xml";
     4 dialog.FileName = "my-class-test";
     5 
     6 var dr = dialog.ShowDialog();
     7 if (dr == System.Windows.Forms.DialogResult.OK)
     8 {
     9     txtFilePath.Text = dialog.FileName;
    10 
    11     using (FileStream fs = File.OpenRead(dialog.FileName))
    12     {
    13         XmlSerializer xmlSerializer = new XmlSerializer(typeof(MyClass));
    14         _myClassInfo = xmlSerializer.Deserialize(fs) as MyClass;
    15         base.DataContext = _myClassInfo;
    16 
    17         this.tabStudents.SelectedIndex = 0;
    18         this.tabTeachers.SelectedIndex = 0;
    19     }
    20 }

    好了,这样程序就已经完成了。你没看错,这已经是几乎所有代码了!是不是很不可思议?你可能已经有心理准备,WPF将会以非常优雅的代码完成我们所需的逻辑,但是这也太神奇了!区区百行代码竟然完成了Winform中可能需要数倍代码量的逻辑!想象中的后台组装MyClass实例并生成XML的代码竟然都已经由WPF的双向绑定方式悄悄帮你做完了!

    看完这个示例,你是否也开始蠢蠢欲动,想自己动手试试写一个属于自己的WPF程序了呢?当然如果你已经等不及了,你也可以先下载附录中的源码运行一下,一睹为快。

    附录

    本文Demo完整源码下载地址(VS2012):http://files.cnblogs.com/files/wushangjue/WpfDemo.zip

  • 相关阅读:
    C语言点滴
    随便记点什么
    STL的使用
    Linux下OTG支持USB摄像头
    socket编程实战-调试
    socket编程实战-bind端口占用问题
    socket编程实战-tcp_tw_recycle问题
    socket编程实战-connect超时问题
    BT[3]-BLE广播详解
    BT[2]-BLE初体验:心率计
  • 原文地址:https://www.cnblogs.com/wushangjue/p/4513342.html
Copyright © 2020-2023  润新知