虽然最近业余时间主要都放在研究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 v2014 vol 14.2.6 发布

    终于支持XE8 了.需要这么长时间吗? New Major Features in 14.2 What's New in VCL Products 14.2 Feature Highlights To ...

  2. vs2010 EF4.0 访问mysql

    需要安装mysql-connector-net-6.3.5 6.8.9的安装完后在dbfirst里找不到对应的提供程序 链接字符串里需要 指定下编码(如果不是gbk的话) <add name=& ...

  3. UVa 11732 "strcmp()" Anyone? (左儿子右兄弟前缀树Trie)

    题意:给定strcmp函数,输入n个字符串,让你用给定的strcmp函数判断字符比较了多少次. 析:题意不理解的可以阅读原题https://uva.onlinejudge.org/index.php? ...

  4. nvarchar,varchar 区别

        char    char是定长的,也就是当你输入的字符小于你指定的数目时,char(8),你输入的字符小于8时,它会再后面补空值.当你输入的字符大于指定的数时,它会截取超出的字符.   nva ...

  5. (并查集)Travel -- hdu -- 5441(2015 ACM/ICPC Asia Regional Changchun Online )

    http://acm.hdu.edu.cn/showproblem.php?pid=5441 Travel Time Limit: 1500/1000 MS (Java/Others)    Memo ...

  6. codeforces 702C Cellular Network 2016-10-15 18:19 104人阅读 评论(0) 收藏

    C. Cellular Network time limit per test 3 seconds memory limit per test 256 megabytes input standard ...

  7. The Activities of September

  8. EBS 中查看LOV中的查询语句

    http://blog.csdn.net/shishun123/article/details/6874824 一直有实施顾问询问我XXForm的XX LOV是什么逻辑,取数SQL是什么来着,以前比较 ...

  9. 【转】Swig Getting Started

    Installation Via NPM: $ npm install swig --save Basic Usage Swig has multiple ways to compile and re ...

  10. C# 委托和事件,简单示例说明问题

    先看看示例效果 按照国际惯例,得先说说概念. 委托(C# 编程指南) 事件(C# 编程指南) 以上内容来自MSDN. 委托源码 [委托] 概念和代码都有了.剩下的就是应用了,要是只知道概念不会用,那还 ...