用WPF山寨折线图,抄?是狠狠的抄
对于没有美术细胞的我来说,抄袭人家的设计或是创意是再平常不过的事,我承认这很无耻,如果在伟大“天朝”的教育体系下还可能升级为道德上的沦丧,毕竟人家搞个东西也不容易,可任务在身,只好下策,脸皮总是在生存以后才拿上台面的。所以也就不避讳了,俺就是抄的。
该图抄袭自 万仓一黍 的在winform中运用FusionCharts图表(一)
一.抄袭模式
尽管样式要抄袭,可代码不能抄袭,因为没有源码^ – ^ 。首先我们先从使用者的角度出发,作为使用者,我希望怎么来使用控件,而且要考虑到WPF的一些特性。线条可以一个个数组给,和传统的控件一样。可我认为这并不符合WPF的组装特性,每个线条可以是自由控制的,可粗可细,可虚线可实线。
如效果为这样的图:
可以是这样的设计,也就是每个控件自己是一层,和其它控件没有直接联系。就连底面也是可以自由搭配的。
使用还有个就是代码的书写,那么在XAML中单个的控件就可以写成这样
<Sample:LineChart LineColor="Orange" LineThickness="20" Datas="{Binding Items}" />
如若要实现多个拼接,把控件放到可以层叠的容器当中就可以了,比如Grid.如果你的数据是集合形式的,还可以借助ItemsControl这样的集合控件。
这里可能已经有人看出来抄袭的是WPF装饰器的模式和概念。
二.抄袭图形
初步设想不错,可还不够灵活,假设每个点的样式可以更改而不仅仅是这种默认的白点,如根据喜好可以设置五角星,小红旗,这怎么办?一般情况下我们会为控件再添加个点的模板属性;在这里我又踟蹰了很久,是否真的需要这么的“一步到位”?在几年前,我曾听说装修现在有个观点叫做“轻装修,重装饰”,就是建好一个架子,里面的东西是可以由着喜好、心情、季节随意搭配。所以我更倾向于控件首先能实现一些比较简单的功能,可如果要更高级的功能和效果应该可以附加上的,毕竟有时候一开始也很难知道用户的喜好,加上有时候时间紧迫就只能先留下框架,以后有机会再讨价还价索要时间。
产品功能暂时可以延期,却不能老是跳票,“永远的毁灭公爵”绝不是好榜样。
对每个点进行模板,设置每个点实际的位置,很容易让我们想到使用ItemsControl,做个放置位置的Panel就可以了, 当然很明显我们并不需要那么多的功能,只要求每个点都能设置一个模板,那么好吧,依葫芦画瓢来个ItemTemplate、ItemTemplateSelector、ItemStringFormat属性,只需要在初始化的时候声明个UIElementCollection变量,在数据存放以后为数据new个性模板就成,如GetContainerForItemOverride()方法。
在这种条件下做个层,可以轻松的看到类似这样的效果,当然音符是我再次发挥抄袭特长的结果,具体的可以看http://xamlbase.com/free-icons.php
对于层来说,还可以有很多的发挥,如下:
对这种应用也可以手到擒来:
不要惊讶,WPF是有API的,使用CombinedGeometry然后用GeometryCombineMode.Exclude就可以了。
因为底色也可以随意混搭,所以加个水印、Logo,背景提示语之类的也不是什么难事。
对于以上的所谓“创意”,当然是抄袭的,参见各大网站统计图。
三.抄袭代码
项目经理有要求图上的所有线条比例要统一,这个要求很合乎常理,可我们的线条都是单独的一个控件,你也许会说让后端计算出一个最大值绑定一下不就OK了,但一般控件都可以达到的效果,凭什么就要让业务干预,毕竟我们分层的原则是UI和业务分离,虽然有时候会有些妥协,但我们也要尽可能的做大完善,水平不就是在这样的“苛责”中进步的么?
感觉不错的抄法
▲名字抄袭
做为前端人员,可能希望只要设置一个属性就可以完成,其实它的目的就是为了功能的附加,感觉有点像ToolTipService吧,那么我们的名字就叫LineChartService。
<Sample:LineChart LineColor="Orange" Sample:LineChartService.GroupName="group1" LineThickness="2" Datas="{Binding Items}" /> <Sample:LineChart LineColor="Green" Sample:LineChartService.GroupName="group1" LineThickness="2" Datas="{Binding Items1}" />
▲做法抄袭
这种方法是方便,可作为后端的编码人员应该怎么做呢?印象中第一个出现的类似应用可能就是RadioButton,因为它也有个GroupName,可以根据组来决定勾选的控件影响的范围,好吧看看它的源码是如何做到的的,其实所谓的看就是一个大抄袭,去其特质取其共性。
▲算法抄袭
在抄袭这一模式的时候,有个想法就是计算出每个控件的最大值,然后放到一个全局的队列中,这个队列有自动排序功能,并有一个属性可以取得最大值.这其实是个算法,这个算法哪有的抄呢?当时在看BeginInvoke,我们的DispatcherPriority放进去,系统会根据值的大小排序,每次有个属性可以取得最大值,这不正是我所期望的么,啥都不说了,直接Copy,当然也要去掉一些东西,这种简单的算法,只要理解了他的目的,也没啥难度。所以PriorityQueue这个内部类也被我惨无人道的抄袭了。
▲WPF特性应用抄袭
对于屏幕的笛卡尔坐标,和我们普通的坐标不同,越向下Y轴值越大,一般的做法是用高度值来减下,以符合需求,可在WPF有Transform,在最后绘制图形的时候用这个来包装下,个人认为更优雅,这一做法抄袭自donjuan的在WPF中使用ItemsControl控件来实现线状图控件(一)
drawingContext.PushTransform(new ScaleTransform(1, -1, 0, RenderSize.Height / 2)); … drawingContext.Pop();
不爽的抄法
在为点应用模板的代码中,最邪恶的应该是以下的一段山寨成果,声明这一坨真不是我的原创,打这一段也打的我老郁闷了,具体可以参看ItemsContorl中的做法。
protected virtual void PrepareContainerForItemOverride(DependencyObject element, double item) { var headeredContentControl = element as HeaderedContentControl; if (headeredContentControl != null) { headeredContentControl.Content = item; headeredContentControl.ContentTemplate = ItemTemplate; headeredContentControl.ContentTemplateSelector = ItemTemplateSelector; headeredContentControl.ContentStringFormat = ItemStringFormat; } else { var contentControl = element as ContentControl; if (contentControl != null) { contentControl.Content = item; contentControl.ContentTemplate = ItemTemplate; contentControl.ContentTemplateSelector = ItemTemplateSelector; contentControl.ContentStringFormat = ItemStringFormat; } else { var contentPresenter = element as ContentPresenter; if (contentPresenter != null) { contentPresenter.Content = item; contentPresenter.ContentTemplate = ItemTemplate; contentPresenter.ContentTemplateSelector = ItemTemplateSelector; contentPresenter.ContentStringFormat = ItemStringFormat; } else { var headeredItemsControl = element as HeaderedItemsControl; if (headeredItemsControl != null) { headeredItemsControl.Header = item; headeredItemsControl.HeaderTemplate = ItemTemplate; headeredItemsControl.HeaderTemplateSelector = ItemTemplateSelector; headeredItemsControl.HeaderStringFormat = ItemStringFormat; } else { ItemsControl itemsControl; if (((itemsControl = element as ItemsControl) != null)) { itemsControl.ItemTemplate = ItemTemplate; itemsControl.ItemTemplateSelector = ItemTemplateSelector; itemsControl.ItemStringFormat = ItemStringFormat; } } } } } }
微软的类似的代码其实并不是最令人愤慨的,大批的internal才是令人反胃的,so,我也就一同copy了这风格。
四.补充
对于折线图的应用还可以有线条的动画呈现,点的动画变动,点的手动拖拉,大数据虚拟化显示等等,这些都可以发挥自己的想象做出更美好的作品,但您也可以抄袭一些现有的控件来达到需求。总之我们“学习”、“参考”都是希望有一天那些东西能够为我们效劳,对于类似左边图上的效果也很容易抄袭得到。
如果您下了我的代码,可能会发现所提供的控件,似乎“只可远观不可亵玩”,很多功能貌似都被阉割了。比如:默认阴影的两头只是简单的处理,阴影里没有圆形的倒影,看起来不真实,默认的圆圈半径和外框线当值设大时会有问题(控件的Pen的Thickness的线的中点落在给定圆的半径上,而不是在半径外有Thickness),GroupName只处理了最大值而未处理最小值,PointLayer控件所产生的模板好像并未和原来的点重合,这么多控件有些方法居然没有提成公用方法等等。首先我得承认这些问题都是刚开始疏忽了,如果您能放弃那些控件而用自己的方式进行山寨,改份更好的,BUG更少的,更易用的,我想您的心情会更好。
回顾这些年的程序员生涯,让我感慨最深的就是复制和粘贴,从最初的Hello World到WPF的学习,一路上就是Sample的抄袭和应用,整个过程都是在别人路上进行徘徊,模式是别人的,语言是别人的,用法也是别人规定的,跌撞中偶尔发现个技法,也是在使用别人的API下,日复一日我都产生了做软件如同搭积木的消极想法。可其实人的物质生活,思想文化又是怎么提高的呢?可以说是站立在伟人的肩膀上,也可以说是把别人发现的路拓展的更好的基础上,生活也就在这一点点变化中,愈加美好。当然,您得拥有一颗不满现状积极向上的心。
PS:因抄袭产生的RP问题概不负责,毕竟上个月俺已经丢了饭卡一张,门进卡一张,手机一部,手机卡补的时候还多花了30元。
附件 尽管我们是同道中人,还是希望您不会再要刻度尺,因为我也没有^^