关于如何解决特定场景下WPF4.0中“XamlWriter.Save序列化限制”问题的一种思路
问题背景
笔者最近使用ArcGIS Server WPF API 开发一个能够实现所见即所得的基于Xaml的图层符号编辑功能。这个功能主要目的能够让用户以Xaml的形式来定义图层符号,并能够在编辑的同时以可视化的方式实时地预览该符号的设计效果,最后将结果保存到以Xaml文本形式存储的符号文件库(一个Xaml文件以ResourceDictionary的方式保存了多个符号)中。同时该功能还允许用户能够编辑和保存位于Xaml文件库中的符号。对于解决这类问题的一般思路往往都是:
1、首先,在系统启动时将位于Xaml文件库中的符号以反序列化的方式转换为.net的内存对象,并进行可视化的显示,这个通过.net 4.0中提供的XamlReader.Load方法就可以轻松实现;
2、然后,在用户需要对选定对象进行编辑的时,再将该对象通过XamlWrite.Save方法序列化成Xaml文本。并在用户修改该Xaml文本的同时实时地反序列化成对象,以达到实时预览的效果;
3、最后,保存时再将最新的Xaml文本保存回Xaml文本库。
这个思路本来没有任何问题,但在实际实施的过程中,笔者发现使用XamlWriter.Save方法序列化得到的Xaml文本与原始的Xaml文本不一致,且再利用XamlRead.Load方法将序列化得到的Xaml文本反序列化后,业务对象无法正常使用。后来经过查阅msdn发现XamlWriter.Save方法在序列化Xaml对象的过程中确实存在诸多限制(详见《XamlWriter.Save的序列化限制》)。因此希望通过XamlWriter.Save来完成符号对象的序列化的路线是行不通的。
问题分析与解决思路
经过仔细地分析,笔者发现该功能中涉及到序列化的对象主要有两种状态:一是新建对象,另一个是Xaml符号库中的对象。对于新建的对象,由于对应的Xaml文本均是用户在程序运行时输入指定,所以在保存阶段只需直接将该文本输出到符号库中即可;对于Xaml符号库中已有的对象,可以考虑在使用XamlReader.Load方法反序列化符号对象的同时液将该对象所对应的Xaml文本保存到一个内存字典中,当用户需要对该符号进行编辑时则直接从内存字典中取出该符号所对应的Xaml文本,最后当需要保存修改结果时,则是将修改后的最新Xaml文本保存回符号库。使用以上过程即可越过XamlWrite.Save来完成符号对象的保存。
实现要点
为了实现上面的思路,笔者设计了一个名为XamlSymbolResourceDictSerializer的类,该类实现了自定义接口IXamlSymbolResourceDictSerializer,接口结构如下:
{
/// <summary>
/// deserialize object from the xaml stream
/// </summary>
/// <returns>return the instance of class ResourceDictionary </returns>
System.Windows.ResourceDictionary Read();
/// <summary>
/// serialize instance into xaml format
/// </summary>
/// <returns></returns>
string Save();
/// <summary>
/// Get or set the stream of the xaml ,the root node of which is ResourceDictionary
/// </summary>
System.IO.Stream Xamlstream { get; set; }
/// <summary>
/// Get the dictionary of symbols in xaml format
/// </summary>
Dictionary<string, string> XamlSymbols
{
get;
}
/// <summary>
/// the footer of the serialize xaml document
/// </summary>
string XmalRDFooter { get; set; }
/// <summary>
/// the hander of the serialize xaml document
/// </summary>
string XmalRDHander { get; set; }
}
在该接口中定义了两个方法:Read和Save,分别用于实现图层符号的反序列化和序列化;
接口中的属性对象:
Xamlstream用于初始化时设置Xaml文本流;
XamlSymbols用于存储该Xaml文本流中各符号对应的Xaml文本的字典;
XamlRDHander和XamlRDFooter用于记录该Xaml文本流中ResourceDictionary的首尾标签,其中首标签中存储了该Xaml文档的命名空间。
系统在实例化类XamlSymbolResourceDictSerializer时,在构造函数中传入Xaml文本流对象(也可使用属性Xamlstream传入),然后调用Read方法来反序列化获取Xaml文本流中的符号对象。
Read方法
在Read方法中主要完成了两个方面的功能:
1、以文本解析的方式来获取:存储该Xaml文本流中各符号对应的Xaml文本的字典和该Xaml文本流中ResourceDictionary的首尾标签;
2、通过XamlReader.Load方法反序列化Xaml文本流,以得到ResourceDictionary实例。
Save方法
在Save方法中主要完成了将记录下的Xaml文本流中ResourceDictionary的首尾标签和各符号对应的Xaml文本的字典进行序列化后的文本输出。
实现代码
{
#region private fields
private Stream xamlstream = null;
private string _xmalRDHander = string.Empty;
private string _xmalRDFooter = "</ResourceDictionary>";
private string xamlText = string.Empty;
private Stream xamlStreamCopy = null;
StreamReader streamCopyReader = null;
#endregion
#region public fields and attributes
private Dictionary<string, string> xamlSymbols = new Dictionary<string, string>();
public Dictionary<string, string> XamlSymbols
{
get { return xamlSymbols; }
set { xamlSymbols = value; }
}
public string XmalRDHander
{
get { return _xmalRDHander; }
set { _xmalRDHander = value; }
}
public string XmalRDFooter
{
get { return _xmalRDFooter; }
set { _xmalRDFooter = value; }
}
public Stream Xamlstream
{
get { return xamlstream; }
set
{
xamlstream = value;
xamlStreamCopy = new MemoryStream();
xamlstream.CopyTo(xamlStreamCopy);
xamlStreamCopy.Position = 0;
xamlstream.Position = 0;
}
}
#endregion
#region contructor
public XamlSymbolResourceDictSerializer(Stream xamlStream)
{
Xamlstream = xamlStream;
}
public XamlSymbolResourceDictSerializer() { }
#endregion
#region public function
public ResourceDictionary Read()
{
InitializeXamlSymbols();
xamlstream.Position = 0;
return XamlReader.Load(xamlstream) as ResourceDictionary;
}
public string Save()
{
StringBuilder sb = new StringBuilder();
sb.AppendLine(_xmalRDHander);
sb.AppendLine(serializeXamlSymbols());
sb.AppendLine(_xmalRDFooter);
return sb.ToString();
}
#endregion
#region private method
private void InitializeXamlSymbols()
{
System.Xaml.XamlXmlReader reader = new System.Xaml.XamlXmlReader(xamlstream);
int linenum = 0;
int linepos = 0;
initializeRDHander(reader);
initializeSymbols(reader);
//initializeRDFooter(reader, ref linenum, ref linepos);
reader.Close();
}
private void initializeSymbols(System.Xaml.XamlXmlReader reader)
{
int beginlinenum = reader.LineNumber;
int beginlinepos = reader.LinePosition - 2;
int endlinenum = -1;
int endlinepos = -1;
string key = string.Empty;
while (reader.Read())
{
if (reader.NodeType == System.Xaml.XamlNodeType.StartObject && reader.Type.Name.Contains("Symbol"))
{
endlinenum = reader.LineNumber;
endlinepos = reader.LinePosition - 2;
int linenum = endlinenum - beginlinenum;
StringBuilder sb = new StringBuilder(linenum);
//int count=endIndex - beginIndex;
//char[] buffer = new char[count];
//streamreader.ReadBlock(buffer, beginIndex, count);
//xmalRDHander = new string(buffer);
//string firstline = streamCopyReader.ReadLine();
//sb.AppendLine(firstline);
for (int i = 0; i < linenum; i++)
{
string linestr = streamCopyReader.ReadLine();
sb.AppendLine(linestr);
}
//char[] buffer = new char[endlinepos + 1];
//string endline = streamCopyReader.ReadLine();
////streamreader.ReadBlock(buffer, (int)streamreader.BaseStream.Position, buffer.Length);
//sb.AppendLine(endline);
string xamlsymbol = sb.ToString();
xamlSymbols.Add(key, xamlsymbol);
beginlinenum = reader.LineNumber;
beginlinepos = reader.LinePosition - 2;
}
if (reader.NodeType == System.Xaml.XamlNodeType.StartMember && reader.Member.Name.ToUpper() == "KEY")
{
if (reader.LineNumber == beginlinenum)
{
while (reader.Read() && reader.NodeType != System.Xaml.XamlNodeType.Value) { }
key = reader.Value.ToString();
}
}
}
}
private void initializeRDHander(System.Xaml.XamlXmlReader reader)
{
//int beginIndex = (int)xamlstream.Position;
int beginlinenum = reader.LineNumber;
int beginlinepos = reader.LinePosition;
int endlinenum = -1;
int endlinepos = -1;
//int endIndex = -99999;
while (reader.Read())
{
if (reader.NodeType == System.Xaml.XamlNodeType.StartObject && reader.Type.Name.Contains("Symbol"))
{
endlinenum = reader.LineNumber;
endlinepos = reader.LinePosition;
break;
}
}
if (endlinenum > 0)
{
xamlStreamCopy.Position = 0;
int linenum = endlinenum - beginlinenum;
StringBuilder sb = new StringBuilder(linenum);
streamCopyReader = new StreamReader(xamlStreamCopy);
//int count=endIndex - beginIndex;
//char[] buffer = new char[count];
//streamreader.ReadBlock(buffer, beginIndex, count);
//xmalRDHander = new string(buffer);
for (int i = 0; i < linenum - 1; i++)
{
string linestr = streamCopyReader.ReadLine();
sb.AppendLine(linestr);
}
//char[] buffer = new char[endlinepos + 1];
//string endline = streamCopyReader.ReadLine();
//streamreader.ReadBlock(buffer, (int)streamreader.BaseStream.Position, buffer.Length);
//sb.AppendLine(endline.Substring(0, endlinepos -2));
_xmalRDHander = sb.ToString();
//xamlStreamCopy.Position = xamlStreamCopy.Position - endline.Length + 1;
}
}
private string serializeXamlSymbols()
{
StringBuilder sb = new StringBuilder(xamlSymbols.Count);
foreach (string xamlsymbol in xamlSymbols.Values)
{
sb.AppendLine(xamlsymbol);
}
return sb.ToString();
}
#endregion
#region IDisposable 成员
public void Dispose()
{
xamlSymbols.Clear();
xamlSymbols = null;
if (xamlstream != null)
{
xamlstream.Dispose();
xamlstream.Close();
}
if (xamlStreamCopy != null)
{
xamlStreamCopy.Dispose();
xamlStreamCopy.Close();
}
if(streamCopyReader!=null)
{
streamCopyReader.Dispose();
streamCopyReader.Close();
}
}
#endregion
}
总结
本文采用的是“回避”战术来解决WPF4.0中“XamlWriter.Save序列化限制”的问题,该战术在本例中确实能够起到一定的积极作用,能够较好地解决图层符号可视化编辑与管理的功能需求,还能避免出现序列化时性能低下的问题。但该战术还存在一个致命的缺陷,就是它与特定的使用场景绑得太紧,不够通用。对于这一点,笔者有些思路:能否通过可配置的方式,让XamlSymbolResourceDictSerializer类能够适应各种xaml文档结构。如果有新的成果,笔者会第一时间与大家分享。如果大家有什么解决xaml序列化问题的好思路,也请和笔者联系,咱们共同学习。
转载于:https://www.cnblogs.com/wowMVP/archive/2010/09/21/1832657.html
关于如何解决特定场景下WPF4.0中“XamlWriter.Save序列化限制”问题的一种思路相关推荐
- 特定场景下Yolo改进算法:Poly-Yolo
论文名称:Poly-YOLO: higher speed, more precise detection and instance segmentation for YOLOv3 论文地址:https ...
- 干货 | 解决分布式场景下数据一致性问题,我有办法!
此次分享的缘由 支付重构 考虑支付重构的时候,自然想到原本属于一个本地事务中的处理,现在要跨应用了要怎么处理.拿充值订单举个栗子吧,假设:原本订单模块和账户模块是放在一起的,现在需要做服务拆分,拆分成 ...
- sqlserver检测到基于一致性的逻辑_面试官;解决分布式场景下数据一致性问题
在这一篇中主要回答目前分布式事务问题是怎么解决的?行业中有什么解决方案?这些解决方案分别有什么优缺点?别人是怎么做的?我们可以怎么来做? 支付重构 考虑支付重构的时候,自然想到原本属于一个本地事务中的 ...
- [Camera Drv]开video dynamic framerate,特定场景下video encode时会闪屏 - MTK物联网在线解答 - 技术论坛
[Camera Drv]开video dynamic frame rate,特定场景下video encode时会闪屏 1. 开 video dynamic frame rate ,环境 BV 在 d ...
- OpenYurt 联手 eKuiper,解决 IoT 场景下边缘流数据处理难题
简介:云计算的出现促使物联网实现爆炸式增长.在设备规模和业务复杂度不断攀升的趋势之下,边缘计算因其能够将计算能力更靠近网络边缘和设备,从而带来云性能成本的降低,也在这波浪潮之下得到快速发展. 作者 | ...
- 先查询后修改并发的时候sql_如何解决并发场景下扣款的数据一致性问题?
1.场景介绍 场景1:扣费,企业账户送流量或者红包,用户签到领取.此场景下就是多用户对某一个账号的并发扣款: 场景2:充值,打赏给主播,这种场景是多用户对同一个账号进行打款,但是方案和问题和场景1是一 ...
- 特定场景下取代if-else和switch的方式
look-up表代替if-else 比如某平台的信用分数评级: 超过700-950,信用极好, 650-700信用优秀, 600-650信用良好, 550-600信用中等, 350-550信用较差. ...
- AliAGC 自动增益控制算法:解决复杂场景下的音量问题
音视频会议,直播连麦以及短视频已经成为人们工作.教学以及娱乐的一部分,其背后都离不开音视频实时通信等关键技术的广泛应用.音频方面,可预见的是客户业务形式的多样性,环境的复杂性,以及接入设备的差异性会带 ...
- Flink1.4.0中反序列化及序列化类变化
Flink1.4.0中,反序列化及序列化时继承的类,有一些被标记为了"@deprecated",路径上也有变化: 1.AbstractDeserializationSchema 以 ...
最新文章
- Matlab与数据结构 -- 对向量的排序
- ACM模板--邻接矩阵 无向图 搜索算法
- PHP的file_put_contents函数把一个字符串写入文件中
- 如何开始了解一个新知识(Vuex)
- echarts柱状图x轴 label一行超过设置的字数换行
- Oracle中给表添加主键 外键,给表中添加主键、外键
- Vue 单页面应用 把公共组件放在 app.vue 但是我希望某个页面没有这些公共组件怎么办???(比如登陆页面)
- java 用户、角色、权限数据库设计
- Atitit.数据索引 的种类以及原理实现机制 索引常用的存储结构
- 1000入门测试题目
- Python编程基础
- 虚拟机无法启动(与设备不兼容)或者启动蓝屏
- Spring Cloud Bus 官方文档
- html 布局 拖拽 在线,可视化编辑 - 拖拽式编辑网页模板无需代码,自由拖拽布局,即可完成网站设计制作!...
- channel的实现原理
- JOIN连接:LEFT OUTER JOIN
- 机器学习和数据科学中常用的公开数据集(含计算机视觉最全数据集汇总)
- 幅相曲线渐近线_若最小相位系统的低频段幅频特性的渐近线是一条斜率为20dB/dec的直线,则该系统( )。_学小易找答案...
- STM32CubeMx + LWIP(实现UDP组播/MQTT/热插拔)系列 二 ----- CubeMx生成文件的简单介绍与热插拔
- 计算机网络 构建Web内容的技术
热门文章
- php 读csv跳过标题,请问怎么使用Python编辑csv文件时跳过标题
- ssm练手小项目_20 个 JavaScript+Html+CSS 练手的小项目
- github上下载别人的vue项目,本地运行
- python | while循环与for循环 | 循环嵌套 | pass通用类型,循环整体结束或开始下一轮循环
- rhel系统启动过程_Linux系统启动过程
- 调用指定目录下的批处理bat_批处理(.bat)的奇技淫巧
- python的常量和变量_python中的常量和变量代码详解
- 数据挖掘原理与算法 Agnes算法
- 胜利大逃亡 三维BFS
- Math.random()取随机数一直为0