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的思路
- 拖控件布局
- 创建控件的各种事件
- 通过后台代码控制界面布局以及元素行为
- 后台代码获取元素的值并将他们赋值给所需的对象
- 写非常复杂的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 = ;
this.tabTeachers.SelectedIndex = ;
}
}
好了,这样程序就已经完成了。你没看错,这已经是几乎所有代码了!是不是很不可思议?你可能已经有心理准备,WPF将会以非常优雅的代码完成我们所需的逻辑,但是这也太神奇了!区区百行代码竟然完成了Winform中可能需要数倍代码量的逻辑!想象中的后台组装MyClass实例并生成XML的代码竟然都已经由WPF的双向绑定方式悄悄帮你做完了!
看完这个示例,你是否也开始蠢蠢欲动,想自己动手试试写一个属于自己的WPF程序了呢?当然如果你已经等不及了,你也可以先下载附录中的源码运行一下,一睹为快。
附录
本文Demo完整源码下载地址(VS2012):http://files.cnblogs.com/files/wushangjue/WpfDemo.zip
WPF快速实现XML可视化编辑工具的更多相关文章
- 一个基于Bootstrap实现的HMTL可视化编辑工具
疫情禁足在家,用原生的JS实现了一个HTML可视化编辑工具,页面布局基于Bootstrap.大约一个月时间,打通主要技术关卡,实现了第一版: 可以拖放编辑,实现了几乎所有的bootstrap预定义 ...
- 我做了一个 HTML 可视化编辑工具,有前途吗?
疫情在家的这段时间,我做了一个 HTML 可视化编辑工具,做的时候信心满满,差不多完成了,现在反而不如以前信心足了,这玩意有用吗?代码地址: https://github.com/vularsoft/ ...
- XML编辑工具
[标题]XML编辑工具 [开发环境]Qt 5.2.0 [概要设计]使用QT的视图/模型结构.treeview控件以树形结构显示所要操作的XML文件,并实现xml的相关操作 [详细设计] 主要包含 no ...
- WPF学习12:基于MVVM Light 制作图形编辑工具(3)
本文是WPF学习11:基于MVVM Light 制作图形编辑工具(2)的后续 这一次的目标是完成 两个任务. 本节完成后的效果: 本文分为三个部分: 1.对之前代码不合理的地方重新设计. 2.图形可选 ...
- WPF学习11:基于MVVM Light 制作图形编辑工具(2)
本文是WPF学习10:基于MVVM Light 制作图形编辑工具(1)的后续 这一次的目标是完成 两个任务. 画布 效果: 画布上,选择的方案是:直接以Image作为画布,使用RenderTarget ...
- 3款强大的BootStrap的可视化制作工具推荐
http://www.25xt.com/html5css3/7342.html 25学堂看到最近很多朋友在学习Bootstrap前端主题框架.顾让25学堂的小编给大家找来了3款适合Bootstrap初 ...
- QuickSwitchSVNClient,快速完成SVN Switch的工具
[开源]QuickSwitchSVNClient,快速完成SVN Switch的工具 在实际的开发中,我们一般使用SVN工具进行源代码的管理.在实际的产品开发中,根据项目的一些定制要求,往往需要对某一 ...
- Dynamics 365 for CRM: Sitemap站点图的可视化编辑功能
Dynamics 365 for CRM 提供了Sitemap站点图的可视化编辑功能 在之前的所有版本中,我们只能通过从系统中导出站点图的XML进行编辑后再导入(容易出错),或使用第三方的Sitema ...
- xml可视化编辑器
——业内首创的在线可视化XML结构化数据编辑方法 Boxth Visual XML Web Editor (Boxth XWE) 是专为在线处理XML结构化数据而设计的在线(Web).可视化(WYSW ...
随机推荐
- 2018.09.30 bzoj2741: 【FOTILE模拟赛】L(分块+可持久化01trie)
传送门 数据结构经典题. 首先考虑另外一种询问方式. 已知权值val,在区间[1,n][1,n][1,n]中找一个数使得valvalval^a[i]a[i]a[i]最大. 这个可以直接01trie. ...
- 一个 图片 滚动 飞入的css特效
@keyframes bounceInLeft { from, 60%, 75%, 90%, to {animation-timing-function: cubic-bezier(0.215, 0. ...
- 快速掌握Java中Lambda表达式的用法
Lambda表达式的作用: Lambda表达式的作用就是简化代码开发,让代码看起来更加简介.它是用来简化匿名内部类的.但是并不是所有的匿名内部类都能用Lambda表达式简化,Lambda表达式是有使用 ...
- [Java]ArrayList集合的contains方法
用到集合ArrayList时经常会用到里面自带的方法boolean contains(Object o);此方法用于判断集合里面是否包含元素o,现在讨论下在Object类型为类类型的时候的情况: cl ...
- 个人封装JavaScript函数
function get_next_day(str_date){ var d=""; d=new Date(str_date); d.setDate(d.getDate()+1); ...
- Activity-fragment-ListView展示
在上一篇博客,Android-fragment简介-fragment的简单使用,介绍了简单的使用: 这篇博客主要讲解,在fragment做处理事情(Activity的事情) Activity pack ...
- zabbix-server启动报错解决
启动zabbix-server有如下报错: 29171:20180714:084911.367 cannot start alert manager service: Cannot bind sock ...
- nodejs+express安装配置(Linux版本)
在ubuntu下面,直接从源里面安装nodejs的话,此版本还行,但是相关的express等,会比较老. 采用源码安装,先下载nodejs的源码,然后三步: ./configure make make ...
- JPA注解指南
PA注解持久化类很方便,需要jar包:ejb3-persistence.jar.我用以下三个类来说明用法. @SuppressWarnings("serial") @Entity ...
- JaveScript之CSS变量
大概是CSS3吧,出了一个叫CSS变量的东西,也叫自定义属性,还是比较有用的东东,可以用JavaScript灵活控制,变量作用 我们来实现一个div跟随鼠标滚动的小东西用来说明如何自定义变量 :roo ...