用WPF山寨折线图,抄?是狠狠的抄

     对于没有美术细胞的我来说,抄袭人家的设计或是创意是再平常不过的事,我承认这很无耻,如果在伟大“天朝”的教育体系下还可能升级为道德上的沦丧,毕竟人家搞个东西也不容易,可任务在身,只好下策,脸皮总是在生存以后才拿上台面的。所以也就不避讳了,俺就是抄的。

     2010032418543844

     该图抄袭自 万仓一黍 的在winform中运用FusionCharts图表(一)

 

一.抄袭模式

 

        尽管样式要抄袭,可代码不能抄袭,因为没有源码^ – ^ 。首先我们先从使用者的角度出发,作为使用者,我希望怎么来使用控件,而且要考虑到WPF的一些特性。线条可以一个个数组给,和传统的控件一样。可我认为这并不符合WPF的组装特性,每个线条可以是自由控制的,可粗可细,可虚线可实线。

如效果为这样的图:

image

 

可以是这样的设计,也就是每个控件自己是一层,和其它控件没有直接联系。就连底面也是可以自由搭配的。

image image

使用还有个就是代码的书写,那么在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

image

对于层来说,还可以有很多的发挥,如下:

image

对这种应用也可以手到擒来:

image

不要惊讶,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了这风格。

 

四.补充

 

2006101510830284

       对于折线图的应用还可以有线条的动画呈现,点的动画变动,点的手动拖拉,大数据虚拟化显示等等,这些都可以发挥自己的想象做出更美好的作品,但您也可以抄袭一些现有的控件来达到需求。总之我们“学习”、“参考”都是希望有一天那些东西能够为我们效劳,对于类似左边图上的效果也很容易抄袭得到。

       如果您下了我的代码,可能会发现所提供的控件,似乎“只可远观不可亵玩”,很多功能貌似都被阉割了。比如:默认阴影的两头只是简单的处理,阴影里没有圆形的倒影,看起来不真实,默认的圆圈半径和外框线当值设大时会有问题(控件的Pen的Thickness的线的中点落在给定圆的半径上,而不是在半径外有Thickness),GroupName只处理了最大值而未处理最小值,PointLayer控件所产生的模板好像并未和原来的点重合,这么多控件有些方法居然没有提成公用方法等等。首先我得承认这些问题都是刚开始疏忽了,如果您能放弃那些控件而用自己的方式进行山寨,改份更好的,BUG更少的,更易用的,我想您的心情会更好。

       回顾这些年的程序员生涯,让我感慨最深的就是复制和粘贴,从最初的Hello World到WPF的学习,一路上就是Sample的抄袭和应用,整个过程都是在别人路上进行徘徊,模式是别人的,语言是别人的,用法也是别人规定的,跌撞中偶尔发现个技法,也是在使用别人的API下,日复一日我都产生了做软件如同搭积木的消极想法。可其实人的物质生活,思想文化又是怎么提高的呢?可以说是站立在伟人的肩膀上,也可以说是把别人发现的路拓展的更好的基础上,生活也就在这一点点变化中,愈加美好。当然,您得拥有一颗不满现状积极向上的心。

      

PS:因抄袭产生的RP问题概不负责,毕竟上个月俺已经丢了饭卡一张,门进卡一张,手机一部,手机卡补的时候还多花了30元。

附件  尽管我们是同道中人,还是希望您不会再要刻度尺,因为我也没有^^

posted @ 2010-04-02 10:21  Curry  阅读(4167)  评论(13编辑  收藏  举报