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\"?>\r\n");
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

时间: 2024-08-30 03:00:44

WPF快速实现XML可视化编辑工具的相关文章

一个基于Bootstrap实现的HMTL可视化编辑工具

疫情禁足在家,用原生的JS实现了一个HTML可视化编辑工具,页面布局基于Bootstrap.大约一个月时间,打通主要技术关卡,实现了第一版: 可以拖放编辑,实现了几乎所有的bootstrap预定义工具类. 设计时,尽量让其可以灵活扩展,支持多主题切换,为自定义控件预留了接口. 还可以作为bootstrap学的辅助工具,在界面上选好配置,可以把相应的class全部显示出来,所见即所得. 后期的主要实现方向: 1.增加尽量多的主题,形成一个主题库 2.结合国内微信使用状况,增加可以微信分享的H5模板

XML常用的工具_LiquidXMLStudio映射XML到C++代码

人类社会,据马克思所说,三要素: 生产力,生产关系和财富再分配. 虽然说,总体来说,我个人认为,财富再分配,是核心是重,但对于吊丝来说,还是关注一下生产力吧.至于爸爸是李刚刚的,考虑一下基它的吧. 生产力的核心,就是工具. 所以,做为一个程序员,掌握工具,是很重要的.以前我招人,也是理论和工具并重.一个不注重工具的人,往往是一个不注重实践的人.赵括理论还不错,但打起仗来,就不成.其中一个原因,就是实践不够,或是不重视实践. 这里我总结一下我自己的心理.如果你不幸进来看到我写的文字,先忍一下我的罗

dui框架开发系列:基于控件组合或继承实现 可视化界面编辑工具 的优劣

大家好,我要介绍的所有知识点都是WINCE/windows触摸屏DUI开源框架constvar(点击下载代码)开发过程中遇到的比较有讨论价值的问题. 本文要讨论的是可视化界面编辑工具与控件实现方式的一些关系. 可视化界面编辑工具是DIRECTUI界面框架不可少的工具,它应当是整个框架的比较重要的一部分.VS中的可视化开发工具很强大,比如用MFC拖出来的界面,接近所见即所得,而且消息事件方法属性的增删改查都很便利,接口也很统一,可以说已经做得非常好了.说实话,平常如果做工具软件对界面没要求的那种,

XML编辑工具

[标题]XML编辑工具 [开发环境]Qt 5.2.0,  C++ [概要设计]使用QT的视图/模型结构.treeview控件以树形结构显示所要操作的XML文件,并实现修改和保存功能 [详细设计] 主要包含 node.h(节点类).model.h(模型类).xml.h(xml操作类) node.h文件 使用两个Qstring字符串变量作为类成员,分别用于表示XML文件的节点名和节点值,一个Node节点表示父节点和一个Qlist列表用于存储孩子节点 #ifndef NODE_H #define NO

WPF学习11:基于MVVM Light 制作图形编辑工具(2)

本文是WPF学习10:基于MVVM Light 制作图形编辑工具(1)的后续 这一次的目标是完成 两个任务. 画布 效果: 画布上,选择的方案是:直接以Image作为画布,使用RenderTargetBitmap绑定为Image的图片源,这样可以为后续的导出图片功能提供很大的便利. 对拖动栏XAML进行如下修改: <ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="

Dynamics 365 for CRM: Sitemap站点图的可视化编辑功能

Dynamics 365 for CRM 提供了Sitemap站点图的可视化编辑功能 在之前的所有版本中,我们只能通过从系统中导出站点图的XML进行编辑后再导入(容易出错),或使用第三方的Sitemap编辑工具进行编辑(非常方便). 在Dynamics 365 for CRM 中原生地提供了站点图的可视化编辑功能,非常强大方便: 1.在默认解决方案或自定义解决方案的"客户端扩展"中,找到"站点地图"记录:如果没有则需要点击"添加现成",添加&qu

Toad for Oracle针对于Oracle数据库的可视化管理工具使用

Toad for Oracle安装包下载地址:http://pan.baidu.com/s/1mgBOLZU 在Oracle应用程序的开发过程中,访问数据库对象和编写SQL程序是一件乏味且耗费时间的工作,对数据库进行日常管理也是需要很多SQL脚本才能完成的.Quest Software为此提供了高效的Oracle应用开发工具-Toad(Tools of Oracle Application Developers).在Toad的新版本中,还加入了DBA(Database Administrator

3款强大的BootStrap的可视化制作工具推荐

http://www.25xt.com/html5css3/7342.html 25学堂看到最近很多朋友在学习Bootstrap前端主题框架.顾让25学堂的小编给大家找来了3款适合Bootstrap初学者使用的可视化工具.希望对大家有所帮助. 第一款Bootstrap的可视化制作工具:Jetstrap Jetstrap是一个基于 Bootstrap 前端框架的可视化设计工具. 通过 Jetstrap制作出来的网页 100% 符合 Bootstrap 标准.比如说你只要设计了电脑端的页面,它会自动

RockWare RockWorks v14.0.0.0 (石油化工岩土工程采矿业的可视化分析工具)

Charm.v11.43.0.12 1CD R&L CAD Services Plate'n'Sheet v4.12.12 eVersion build 22.06.2018 1CD Scientific Toolworks Understand 4.0.895 Win32_64 2CD Autodesk EAGLE Premium 9.2.0 Win64 1CD Chasm Consulting VentSim Premium Design v5.1.1.2 1CD Datablast Rel