虽然最近业余时间主要都放在研究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的基类,所有实体类将继承该基类。

     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可视化编辑工具的更多相关文章

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

    疫情禁足在家,用原生的JS实现了一个HTML可视化编辑工具,页面布局基于Bootstrap.大约一个月时间,打通主要技术关卡,实现了第一版:   可以拖放编辑,实现了几乎所有的bootstrap预定义 ...

  2. 我做了一个 HTML 可视化编辑工具,有前途吗?

    疫情在家的这段时间,我做了一个 HTML 可视化编辑工具,做的时候信心满满,差不多完成了,现在反而不如以前信心足了,这玩意有用吗?代码地址: https://github.com/vularsoft/ ...

  3. XML编辑工具

    [标题]XML编辑工具 [开发环境]Qt 5.2.0 [概要设计]使用QT的视图/模型结构.treeview控件以树形结构显示所要操作的XML文件,并实现xml的相关操作 [详细设计] 主要包含 no ...

  4. WPF学习12:基于MVVM Light 制作图形编辑工具(3)

    本文是WPF学习11:基于MVVM Light 制作图形编辑工具(2)的后续 这一次的目标是完成 两个任务. 本节完成后的效果: 本文分为三个部分: 1.对之前代码不合理的地方重新设计. 2.图形可选 ...

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

    本文是WPF学习10:基于MVVM Light 制作图形编辑工具(1)的后续 这一次的目标是完成 两个任务. 画布 效果: 画布上,选择的方案是:直接以Image作为画布,使用RenderTarget ...

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

    http://www.25xt.com/html5css3/7342.html 25学堂看到最近很多朋友在学习Bootstrap前端主题框架.顾让25学堂的小编给大家找来了3款适合Bootstrap初 ...

  7. QuickSwitchSVNClient,快速完成SVN Switch的工具

    [开源]QuickSwitchSVNClient,快速完成SVN Switch的工具 在实际的开发中,我们一般使用SVN工具进行源代码的管理.在实际的产品开发中,根据项目的一些定制要求,往往需要对某一 ...

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

    Dynamics 365 for CRM 提供了Sitemap站点图的可视化编辑功能 在之前的所有版本中,我们只能通过从系统中导出站点图的XML进行编辑后再导入(容易出错),或使用第三方的Sitema ...

  9. xml可视化编辑器

    ——业内首创的在线可视化XML结构化数据编辑方法 Boxth Visual XML Web Editor (Boxth XWE) 是专为在线处理XML结构化数据而设计的在线(Web).可视化(WYSW ...

随机推荐

  1. Devexpress VCL Build v2013 vol 14.1.5 发布

    What's New in 14.1.5 (VCL Product Line)   New Major Features in 14.1 What's New in VCL Products 14.1 ...

  2. 用winsw让任何Windows程序都能运行为服务

    用winsw让任何Windows程序都能运行为服务 winsw介绍 有时候我们需要在Windows下开机运行某些程序,这对于有图形界面的程序来说一般不是什么事,在选项中选中开机启动,然后它们就可以自动 ...

  3. 2018.09.17 atcoder Digit Sum(数论)

    传送门 数论好题啊. 首先对于b<=sqrt(n)b<=sqrt(n)b<=sqrt(n)的情况直接枚举b判断一下就行了. 下面谈一谈如何解决b>sqrt(n)b>sqr ...

  4. 继承方法-->一级一级继承

    Grand.prototype.lastName = 'ji'; function Grand(){}; var grand = new Grand(); Father.prototype = gra ...

  5. modelsim仿真中Altera库的用法

    添加altera 库 实例: 把建立lpm_mux IP时生成的.v文件lpm_mux_ip.v和编写的测试脚本文件放在一起,在modelsim中建立工程,把下面两个文件添加到工程中 直接compil ...

  6. struts2 action 中autowired 不能注入

    一.pom.xml <dependency> <groupId>org.apache.struts</groupId> <artifactId>stru ...

  7. Spring cache 缓存

    概述 Spring 3.1 引入了激动人心的基于注释(annotation)的缓存(cache)技术,它本质上不是一个具体的缓存实现方案(例如 EHCache 或者 OSCache),而是一个对缓存使 ...

  8. [笔记]学习EBS建议有的知识

    http://f.dataguru.cn/thread-51057-1-1.html ORACLE EBS学习的其他资源有哪四个? ORACLE OPEN WORLD大会是不是一个市场营销活动? Or ...

  9. 【转】PHP操作MongoDB【NoSQL】

    原文:http://blog.sina.com.cn/s/blog_4b67d3240101519b.html 一.MongoDB简介 MongoDB (名称来自"humongous&quo ...

  10. Visual Studio 6 (VC6)连接Team Foundation Server (TFS 2018),实现源代码的版本管理

    1. 概述 Visual Studio 6(VB6, VC6, Foxpro-)是微软公司在1998年推出的一款基于Windows平台的软件开发工具,也是微软推出.NET开发框架之前的最后一个IDE工 ...