WPF快速实现XML可视化编辑工具
2015-05-19 10:22
281 查看
虽然最近业余时间主要都放在研究AngularJS上了,不过由于正好要帮朋友做一个生成XML的小工具,顺便又温顾了一下WPF。虽然这个时代相对于Web应用和移动App,Windows应用程序是越来越少了,但是微软并未因此放弃它,反而推出了强大的WPF,让Windows应用程序的制作变得更优雅、更高效。
在我看来,WPF最大的强项就是布局和绑定了。WPF引入了MVVM的编程模式,并结合页面绑定,让UI和业务逻辑完全可以分离由不同的人去完成,而且只要View-Model保持稳定,对于View的布局变动将不受任何限制。因此WPF的编程思维和Winform已经完全不一样,如果你是一个从来没用过WPF的Winform程序员,你首要要做的应该是改变你的思维模式。
创建控件的各种事件
通过后台代码控制界面布局以及元素行为
后台代码获取元素的值并将他们赋值给所需的对象
写非常复杂的if-else逻辑生成所需的XML
如果需要将生成的XML重新绑定到页面上,又是写一遍非常复杂的逻辑进行页面控件赋值
创建实例并将它设置为View的DataContext
将实例的各个属性绑定到View的各个控件中
按需在界面上填写数据后,将实例序列化成XML
如果需要将生成的XML重新绑定到页面上,将文件内容反序列化为实例对象,绑定到DataContext即可
光看文字描述,对于不熟悉WPF的人来说可能很难分清他们的区别,我们看下具体代码吧。为了简化业务,我重新写了一个Demo,通过页面上输入班级、老师、学生信息,生成一个包含班级信息的XML文件。
班级类(老师、学生属性使用ObservableCollection集合,可以使集合变动时界面也自动刷新布局):
老师类:
学生类:
OK,至此为止,我们Demo所需的XML实体类已抽象完毕。
对,你没看错,这一步就是如此简单!其实就注释的那2行代码而已!
老师信息界面代码:
学生信息界面代码:
好了,这样程序就已经完成了。你没看错,这已经是几乎所有代码了!是不是很不可思议?你可能已经有心理准备,WPF将会以非常优雅的代码完成我们所需的逻辑,但是这也太神奇了!区区百行代码竟然完成了Winform中可能需要数倍代码量的逻辑!想象中的后台组装MyClass实例并生成XML的代码竟然都已经由WPF的双向绑定方式悄悄帮你做完了!
看完这个示例,你是否也开始蠢蠢欲动,想自己动手试试写一个属于自己的WPF程序了呢?当然如果你已经等不及了,你也可以先下载附录中的源码运行一下,一睹为快。
在我看来,WPF最大的强项就是布局和绑定了。WPF引入了MVVM的编程模式,并结合页面绑定,让UI和业务逻辑完全可以分离由不同的人去完成,而且只要View-Model保持稳定,对于View的布局变动将不受任何限制。因此WPF的编程思维和Winform已经完全不一样,如果你是一个从来没用过WPF的Winform程序员,你首要要做的应该是改变你的思维模式。
案例需求
需要一个工具,按照某机构的官方文档要求,数据由用户通过程序界面输入,最终生成指定格式的XML文件(某机构已提供XSLT文件,因此生成的XML可以在浏览器中展示出统一的格式。关于XSLT并不在本篇讨论范围内,以后有机会可以另外开篇再说)。Winform的思路
拖控件布局创建控件的各种事件
通过后台代码控制界面布局以及元素行为
后台代码获取元素的值并将他们赋值给所需的对象
写非常复杂的if-else逻辑生成所需的XML
如果需要将生成的XML重新绑定到页面上,又是写一遍非常复杂的逻辑进行页面控件赋值
WPF的思维
将XML抽象成实体类创建实例并将它设置为View的DataContext
将实例的各个属性绑定到View的各个控件中
按需在界面上填写数据后,将实例序列化成XML
如果需要将生成的XML重新绑定到页面上,将文件内容反序列化为实例对象,绑定到DataContext即可
光看文字描述,对于不熟悉WPF的人来说可能很难分清他们的区别,我们看下具体代码吧。为了简化业务,我重新写了一个Demo,通过页面上输入班级、老师、学生信息,生成一个包含班级信息的XML文件。
步骤1:抽象XML实体对象
为了能让实体对象的实例最终和View进行自动的双向绑定,我们需要将所有实体类实现INotifyPropertyChanged接口,为了进一步抽象代码,我们首先创建一个实现了INotifyPropertyChanged的基类,所有实体类将继承该基类。public abstract class ClassBase : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; protected void NotifyPropertyChange(string propertyName) { if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } }
班级类(老师、学生属性使用ObservableCollection集合,可以使集合变动时界面也自动刷新布局):
[XmlRoot(ElementName = "class")] public class MyClass : ClassBase { private string _grade; [XmlAttribute(AttributeName = "grade", Namespace = "")] public string Grade { get { return _grade; } set { _grade = value; NotifyPropertyChange("Grade"); } } private string _classID; [XmlAttribute(AttributeName = "class-id", Namespace = "")] public string ClassID { get { return _classID; } set { _classID = value; NotifyPropertyChange("ClassID"); } } private ObservableCollection<MyTeacher> _teachers; [XmlElement(ElementName = "teachers", Namespace = "")] public ObservableCollection<MyTeacher> Teachers { get { return _teachers; } set { _teachers = value; NotifyPropertyChange("Teachers"); } } private ObservableCollection<MyStudent> _students; [XmlElement(ElementName = "students", Namespace = "")] public ObservableCollection<MyStudent> Students { get { return _students; } set { _students = value; NotifyPropertyChange("Students"); } } }
老师类:
[XmlRoot(ElementName = "teacher")] public class MyTeacher : ClassBase { private string _name; [XmlElement(ElementName = "name", Namespace = "")] public string Name { get { return _name; } set { _name = value; NotifyPropertyChange("Name"); } } private string _teachingFor; [XmlElement(ElementName = "teaching-for", Namespace = "")] public string TeachingFor { get { return _teachingFor; } set { _teachingFor = value; NotifyPropertyChange("TeachingFor"); } } private string _comments; [XmlElement(ElementName = "comments", Namespace = "")] public string Comments { get { return _comments; } set { _comments = value; NotifyPropertyChange("Comments"); } } }
学生类:
[XmlRoot(ElementName = "student")] public class MyStudent : ClassBase { private string _name; [XmlElement(ElementName = "name", Namespace = "")] public string Name { get { return _name; } set { _name = value; NotifyPropertyChange("Name"); } } private int _age; [XmlElement(ElementName = "age", Namespace = "")] public int Age { get { return _age; } set { _age = value; NotifyPropertyChange("Age"); } } private string _gender; [XmlElement(ElementName = "gender", Namespace = "")] public string Gender { get { return _gender; } set { _gender = value; NotifyPropertyChange("Gender"); } } }
OK,至此为止,我们Demo所需的XML实体类已抽象完毕。
步骤2:创建实例并将它设置为View的DataContext
public partial class MainWindow : Window { // 创建空实例 private MyClass _myClassInfo = new MyClass(); public MainWindow() { InitializeComponent(); //将空实例设置为View的DataContext base.DataContext = _myClassInfo; } }
对,你没看错,这一步就是如此简单!其实就注释的那2行代码而已!
步骤3:将实例的各个属性绑定到View的各个空间中
班级信息界面代码:<TextBlock Grid.Row="0" Grid.Column="0" Text="Grade:"></TextBlock> <ComboBox Grid.Row="0" Grid.Column="1" Text="{Binding Path=Grade}"> <ComboBoxItem Content="Grade 1"></ComboBoxItem> <ComboBoxItem Content="Grade 2"></ComboBoxItem> <ComboBoxItem Content="Grade 3"></ComboBoxItem> <ComboBoxItem Content="Grade 4"></ComboBoxItem> <ComboBoxItem Content="Grade 5"></ComboBoxItem> </ComboBox> <TextBlock Grid.Row="1" Grid.Column="0" Text="ClassID:"></TextBlock> <TextBox Grid.Row="1" Grid.Column="1" Text="{Binding Path=ClassID}"></TextBox>
老师信息界面代码:
<GroupBox Header="Teachers" Grid.Row="2" Grid.ColumnSpan="2"> <ContentControl> <Grid> <Grid.RowDefinitions> <RowDefinition Height="*"></RowDefinition> <RowDefinition Height="30"></RowDefinition> </Grid.RowDefinitions> <TabControl x:Name="tabTeachers" ItemsSource="{Binding Path=Teachers}"> <TabControl.ItemTemplate> <DataTemplate> <TextBlock Text="{Binding Path=Name, Converter={StaticResource TeacherNameConverter}}" MinWidth="30"></TextBlock> </DataTemplate> </TabControl.ItemTemplate> <TabControl.ContentTemplate> <DataTemplate> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="160"></ColumnDefinition> <ColumnDefinition Width="*"></ColumnDefinition> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="30"></RowDefinition> <RowDefinition Height="30"></RowDefinition> <RowDefinition Height="30"></RowDefinition> </Grid.RowDefinitions> <Label Grid.Row="0" Grid.Column="0" Content="Teacher name"></Label> <TextBox Grid.Row="0" Grid.Column="1" Text="{Binding Path=Name}"></TextBox> <Label Grid.Row="1" Grid.Column="0" Content="Teaching for"></Label> <TextBox Grid.Row="1" Grid.Column="1" Text="{Binding Path=TeachingFor}"></TextBox> <Label Grid.Row="2" Grid.Column="0" Content="Comments"></Label> <TextBox Grid.Row="2" Grid.Column="1" Text="{Binding Path=Comments}"></TextBox> </Grid> </DataTemplate> </TabControl.ContentTemplate> </TabControl> <StackPanel Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Center"> <Button Name="btnNewTeacher" Content="Create New Teacher" Width="150" Margin="0,0,20,0" Click="btnNewTeacher_Click"></Button> <Button Name="btnDeleteTeacher" Content="Delete Current Teacher" Width="150" Click="btnDeleteTeacher_Click"></Button> </StackPanel> </Grid> </ContentControl> </GroupBox>
学生信息界面代码:
<GroupBox Header="Students" Grid.Row="3" Grid.ColumnSpan="2"> <ContentControl> <Grid> <Grid.RowDefinitions> <RowDefinition Height="*"></RowDefinition> <RowDefinition Height="30"></RowDefinition> </Grid.RowDefinitions> <TabControl x:Name="tabStudents" ItemsSource="{Binding Path=Students}"> <TabControl.ItemTemplate> <DataTemplate> <TextBlock Text="{Binding Path=Name, Converter={StaticResource StudentNameConverter}}" MinWidth="30"></TextBlock> </DataTemplate> </TabControl.ItemTemplate> <TabControl.ContentTemplate> <DataTemplate> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="160"></ColumnDefinition> <ColumnDefinition Width="*"></ColumnDefinition> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="30"></RowDefinition> <RowDefinition Height="30"></RowDefinition> <RowDefinition Height="30"></RowDefinition> </Grid.RowDefinitions> <Label Grid.Row="0" Grid.Column="0" Content="Student name"></Label> <TextBox Grid.Row="0" Grid.Column="1" Text="{Binding Path=Name}"></TextBox> <Label Grid.Row="1" Grid.Column="0" Content="Age"></Label> <TextBox Grid.Row="1" Grid.Column="1" Text="{Binding Path=Age}"></TextBox> <Label Grid.Row="2" Grid.Column="0" Content="Gender"></Label> <ComboBox Grid.Row="2" Grid.Column="1" Text="{Binding Path=Gender}"> <ComboBoxItem Content="Male"></ComboBoxItem> <ComboBoxItem Content="Female"></ComboBoxItem> </ComboBox> </Grid> </DataTemplate> </TabControl.ContentTemplate> </TabControl> <StackPanel Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Center"> <Button Name="btnNewStudent" Content="Create New Student" Width="150" Margin="0,0,20,0" Click="btnNewStudent_Click"></Button> <Button Name="btnDeleteStudent" Content="Delete Current Student" Width="150" Click="btnDeleteStudent_Click"></Button> </StackPanel> </Grid> </ContentControl> </GroupBox>
步骤4:按需在界面上填写数据后,将实例序列化成XML
string xmlFilePath = txtFilePath.Text.Trim(); if (!string.IsNullOrEmpty(xmlFilePath)) { XmlWriterSettings settings = new XmlWriterSettings() { Encoding = Encoding.UTF8, OmitXmlDeclaration = true, NewLineOnAttributes = true, Indent = true, ConformanceLevel = ConformanceLevel.Document }; XmlSerializerNamespaces ns = new XmlSerializerNamespaces(); ns.Add("", ""); using (FileStream fs = new FileStream(xmlFilePath, FileMode.Create)) using (var writer = XmlWriter.Create(fs, settings)) { writer.WriteRaw("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"); XmlSerializer xmlSerializer = new XmlSerializer(typeof(MyClass)); xmlSerializer.Serialize(writer, _myClassInfo, ns); System.Windows.Forms.MessageBox.Show("Success!"); } } else { System.Windows.Forms.MessageBox.Show("Choose a file path to save!"); }
步骤5:读取已有的XML绑定到页面上
OpenFileDialog dialog = new OpenFileDialog(); dialog.DefaultExt = "xml"; dialog.Filter = "XML documents (*.xml)|*.xml"; dialog.FileName = "my-class-test"; var dr = dialog.ShowDialog(); if (dr == System.Windows.Forms.DialogResult.OK) { txtFilePath.Text = dialog.FileName; using (FileStream fs = File.OpenRead(dialog.FileName)) { XmlSerializer xmlSerializer = new XmlSerializer(typeof(MyClass)); _myClassInfo = xmlSerializer.Deserialize(fs) as MyClass; base.DataContext = _myClassInfo; this.tabStudents.SelectedIndex = 0; this.tabTeachers.SelectedIndex = 0; } }
好了,这样程序就已经完成了。你没看错,这已经是几乎所有代码了!是不是很不可思议?你可能已经有心理准备,WPF将会以非常优雅的代码完成我们所需的逻辑,但是这也太神奇了!区区百行代码竟然完成了Winform中可能需要数倍代码量的逻辑!想象中的后台组装MyClass实例并生成XML的代码竟然都已经由WPF的双向绑定方式悄悄帮你做完了!
看完这个示例,你是否也开始蠢蠢欲动,想自己动手试试写一个属于自己的WPF程序了呢?当然如果你已经等不及了,你也可以先下载附录中的源码运行一下,一睹为快。
附录
本文Demo完整源码下载地址(VS2012):http://files.cnblogs.com/files/wushangjue/WpfDemo.zip相关文章推荐
- XML可视化在线编辑工具
- 优秀的XML可视化编辑在线编辑工具介绍
- WPF快速入门系列(9)——WPF任务管理工具实现
- dui框架开发系列:基于控件组合或继承实现 可视化界面编辑工具 的优劣
- 快速原型工具 原型可视化
- 基于Qt的截图工具,实现截图后进行编辑
- xml的开发和编辑工具
- 利用XML配置实现增删改查的.net快速开发架构 简单的构建信息管理系统架构
- java基于poi实现快速操作Excel的工具[v2.0.0]
- PlantUML —— 应用于 Eclipse 的简单快速的 UML 编辑软件,astah 很强大,UML,MAINMIND, CLASSDesign等都可以实现。
- 信息管理系统开发架构 配置实现列表展示分析图形及编辑等 构建信息分析展示平台 C#快速开发架构
- 基于Dom4J实现XML快速解析(一)
- win7系统怎么分?DiskGenius工具快速实现win7分区教程
- 基于Qt的截图工具,实现截图后进行编辑
- 「工具」Dubbo可视化测试工具的设计和实现
- 怎么实现CorelDRAW中轮廓图工具的快速运用
- 脱离SSIS强大的可视化编辑功能,后台C#实现包的创建-爽一把
- 文件读写工具简单实现(一)之java的UI界面可视化画图/制作
- 在WPF中快速实现键盘钩子
- 用HTML5实现的在线编辑工具