Shuhari

WPF Grid指定单元格的方式是一个糟糕的设计

最近在自己的项目里尝试使用了WPF,说实话,感觉并不好。这里想谈谈目前碰到的最主要的问题,即:Grid指定单元格的设计方式并不合理。

回想一下我们在网页里是怎么指定单元格的。语法大概是这样:

<table>
    
<tr>
        
<td></td>
    
</tr>
</table>

 

而在WPF中指定这样的布局,要用一种完全不同的语法:


<Grid>
    
<Grid.RowDefinations>
        
<RowDefination  />
    
</Grid.RowDefinations>
    
<Grid.ColumnDefinations>
        
<ColumnDefination  />
    
</Grid.ColumnDefinations>
    
<TextBlock Grid.Row="0" Grid.Column="1" />
</Grid>

 

在这个项目之前,我已经了解了这种语法,并且(想像中)觉得它应该是有好处的,毕竟比起HTML来说,嵌套层次少了两层,应该比HTML更清楚吧。然而真正用的时候,我才发现这是错的!

 

问题在哪里呢?

原因之一:实际的XAML 相当冗长,每一个控件的常规属性、数据绑定表达式、事件处理函数用属性来表达,经常是密密麻麻的一大串。把样式提取出来也没有多大帮助,因为能提取的东西是有限的,而WPF累赘的数据绑定语法使得这种现象更加恶化。在这一大堆属性里面要找到哪一个Row和Column可真不是件愉快的事情——昨天当我调整完几个大的Grid以后,已经几乎没力气去看别的代码了。

HTML 其实要好看一些,虽然结构上的嵌套多了两层,但是哪个属于哪个基本上是一目了然的,因为结构上已经清楚了,看的时候不会也受到其他属性的干扰。最终我发现,减少嵌套层次并没有带来实际上的好处——虽然HTML的table嵌套到三层以上就已经很难理解了,可WPF也是一样。

 

原因之二:如果Grid最后添加一行,还要再翻到前面的RowDefinations再添加该行的定义,这是何必!因为对Grid最常见的操作就是添加行(或列),这样翻上翻下的额外工作实属多余。HTML不会有这种额外的负担。

 

原因之三:WPF它要求你自己记住控件和行列的对应关系,但是这样又带来一项额外的工作:如果在Grid中间插入一行,那么后面所有的行号都要跟着修改!幸好这种情况目前出现还不是很多,只有两次,但是这么一项简单的工作要付出的代价实在太大了。同样,HTML要插入一行也不需要什么其他的工作。

 

工作做完之后,我又考虑了一下 ,WPF这样设计究竟有什么好处?从前看到过的理由似乎没有什么特别站得住脚的。如果说原因是可以跳过空的单元格不指定,但实际上Grid中要空出的单元格通常远远少于要填充的单元格,而每个单元格都要设定Row和Column,这是为了满足20%的特殊问题而把80%的情况复杂化了,更何况HTML里指定一个空的<td></td>也算不上什么负担。如果说是为了简单修改一个属性就能够移动到别的位置,可实际操作的时候我修改属性以后几乎总是要跟着再操作一步,即把它移动到和标签出现顺序对应的位置上去。因为我发现单元格在文档中和在容器中的顺序如果不一致的话,只会让人糊涂,而且会增大以后犯错的机会。

 

结论:从项目实际操作结果来看,我只能认为Grid这样的设计是糟糕的。

 

 我得承认:自己使用WPF的时间并不长,理解也比较有限,所以这里的想法很有可能是错的。但我还是想说说自己用到目前为止的感觉,我觉得WPF有可能在方向上就错了。虽然WPF在设计上极端强调灵活性和扩展性,但也付出了代价——简单性和可读性在很大程度上丧失了。同样是设置样式,Style拖沓的语法和CSS的简洁性实在不可同日而语。那么这样冗长的语法到底带来了什么好处呢?另一方面,即使M$自己的工具也还不能够很好的适应这种新框架,面对复杂的绑定语法、模板、样式设定,可视化设计工具几乎无用武之地,许多东西还是不得不需要自己手工敲入,而且我已经看到比较复杂的XAML到了令人难以卒读的地步。

posted on 2009-06-15 00:30 Shuhari 阅读(3118) 评论(32) 编辑 收藏

评论

#1楼 2009-06-15 03:35 斯克迪亚      

我怎么不记得这个叫GridPanel~应该是Grid啊:http://msdn.microsoft.com/zh-cn/library/system.windows.controls.grid.aspx

根本原因是两条:
1你还没用熟控件。我刚开始的时候也顶烦Grid了,但是掌握技巧后你就发现这玩意真的很强大,但适合全局布局,不太适合细部布局(麻烦是一方面,更主要的是消耗资源较多)。
2你用错控件了。你想实现的效果用StackPanel+DockPanel合适。

有兴趣可以参看一下我博客里的文章,有两篇关于布局的。

对于WPF,其样式定义功能是革命性的,CSS还相差太多,真要比的话也得XSL来比。
WPF适合数据驱动开发,前后台间可以达成极佳的协作,适合分工明晰的团队进行开发。
XAML虽然写起来比较冗杂,但是也难想出什么更好的语法规则。
MS的工具支持目前的确很不到位,VS2008 SP1开发WPF十分痛苦,反应迟钝、经常报错、属性面板设计支持较差、智能感知无法很好地支持XAML,Blend代码方面十分薄弱,VS 2010据说性能也不咋样,而且至今也没有一个官方的3D设计器、流文档编辑器,有点虎头蛇尾的感觉。
 回复 引用 查看   

#2楼 2009-06-15 07:38 Nick Wang (懒人王)      

@斯克迪亚
这跟用不用熟、用没用对没关系,总有需要用Grid的时候,不方便就是不方便。
我猜还是为了支持可视化设计器,因为这样的代码比嵌套的table好解析和修改。
 回复 引用 查看   

#3楼 2009-06-15 08:18 neuhawk      

我也觉得插入一行很麻烦啊,
而datagrid又不支持合并行合并列。这个grid动态插入行、插入列太麻烦了,
 回复 引用 查看   

#4楼[楼主] 2009-06-15 08:33 Shuhari      

@斯克迪亚:

你说的对,应该是Grid,不是GridPanel。
我这篇文章不是在开发机器上写的,只能靠印象,WPF的容器大多是Panel结尾的,已经成了习惯,所以写Grid的时候也本能的加上去了。

我这个项目的布局要求多行多列对齐,并且有些地方需要Splitter,这除了Grid之外其他的容器都是办不到的,所以尽管Grid很复杂,还是不得不用它。

如果说你想不到什么更好的语法规则,我推荐你看一下Flex的MXML,虽然它的绑定功能要比WPF弱一些,但语法简洁多了。
 回复 引用 查看   

#5楼 2009-06-15 08:54 黄泉天上来      

你用了Blend了吗?  回复 引用 查看   

#6楼[楼主] 2009-06-15 09:21 Shuhari      

@黄泉天上来:

用过了,那又如何?
 回复 引用 查看   

#7楼 2009-06-15 09:36 neuhawk      

看来table控件要自己写了。  回复 引用 查看   

#8楼 2009-06-15 10:35 Clingingboy      

不要以html的方式去思考xaml.不一样的东西  回复 引用 查看   

#9楼 2009-06-15 10:50 progame      

我想设计组肯定不是没考虑过原始html table的模型
但为什么采用现在这种书写起来比较麻烦的模型
(尤其是扩展属性grid.row grid.column的设置)
个人认为:
1, 解析起来更加快速和方便
2, 减少容器的层级, 要知道xaml的事件机制和以前有很大的不同了, 层级越多,事件处理越损耗性能, 再加上triger, style这些都会和层级有很大的相关性

不知道这个猜想是否成立, 仅供参考
 回复 引用 查看   

#10楼 2009-06-15 11:18 学习..      

记得看过报道,WPF是几个MS的技术专家搞出来的。既然是技术专家,那就出处体现技术强行了,比如Markup Extension,XPath的应用。至于易用性,学习曲线,那不是技术专家考虑的问题。我个人对WPF一直不看好,甚至想说,那天MS宣布,不支持这个东东了,因为是一个失败的技术 :)  回复 引用 查看   

#11楼[楼主] 2009-06-15 11:57 Shuhari      

@progame:

我觉得应当是想和其他容器保持一致的原因。比如DockPanel用依赖属性是合适的,因为DockPanel没有什么东西需要嵌套。WPF小组大概是由此希望所有布局属性都用依赖属性来解决。



@学习..
同意你的看法,而且我也觉得,既然MS现在可以(在很大程度上)放弃还没有完全流行起来的WinForm,那么将来哪一天放弃WPF也不是不可能的事情。
 回复 引用 查看   

#12楼 2009-06-15 12:18 neuhawk      

我不喜欢winform。语法上还是喜欢html、  回复 引用 查看   

#13楼 2009-06-15 12:35 周巍      

The grid control in the old version of WPF is not well, but in the new version of WPF, MS added a new DataGrid control.

Further, I always think that the first version of MS product is less usability, for example, WPF, WF and MVC etc., we could not use them in the production environment.
 回复 引用 查看   

#14楼[楼主] 2009-06-15 13:06 Shuhari      

@周巍:

以前听说过WPF Toolkit里面有个DataGrid,或许将来会合并到核心组件?不过Grid纯粹是用来布局的,而DataGrid应该是用来显示数据的,它们还不能算同一个范围的东西。

在第二版完全抛弃上一版的东西也是MS的老毛病了,以前Web Form有过(DataGrid -> GridView),WinForm也有过(ToolBar -> ToolStrip),如果MS真的在下一版又推一个全新的东西,我也不会觉得奇怪。只是WPF已经不能算第一版,3.0到3.5 SP1也有一定时间跨度了,如果MS现在才意识到有问题,那未免有点晚了。
 回复 引用 查看   

#15楼 2009-06-15 14:25 neuhawk      

DataGrid不支持合并行,合并列?  回复 引用 查看   

#16楼 2009-06-15 14:29 斯克迪亚      

我认为Grid是非常优秀的,相对它的功能而言,它的语法还不是那么糟糕。
假如我们自己创造一个基于网格并支持精确/等比/自动/液态填充的布局控件,我们能设计的比Grid优秀而简洁?
 回复 引用 查看   

#17楼 2009-06-15 16:25 Muse      

所有的原因都是一个原因——行列位置的问题。楼主写XAML的时候从来不用设计界面的吗?

打开设计界面,鼠标点击那个控件,XAML的文本自动跳转到那个控件位置,不需要找。而在设计的时候,先自己设计好基本的界面结构,在设计街面上处理,然后再去写详细内容。这样就轻松多了。如果用Blend,会更容易。
添加行也是一样的。
 回复 引用 查看   

#18楼[楼主] 2009-06-15 16:44 Shuhari      

@neuhawk:
我没有实际用过DataGrid,印象里似乎是不支持


@斯克迪亚:
相比HTML table和Flex Grid,我没有看出来WPF Grid究竟优秀和简洁在哪里。HTML和Flex我都用了很久了,table和Grid功能基本上差不多,从来没觉得它们有什么麻烦,只在用WPF的时候第一次有这种感觉。
 回复 引用 查看   

#19楼[楼主] 2009-06-15 16:52 Shuhari      

@Muse:

项目开始的时候只能设计一个大体的界面布局,以后则都是琐碎的细节修改,甚至有的局部界面要推倒重来。所以设计好界面再去做这种方式只有在初期比较有作用。维护的过程中,通常都是手工改比界面上去改更快。

如果能够全部让工具搞定,那简直太幸福了,但是在我的项目里,这种事情从来没有出现过。
 回复 引用 查看   

#20楼 2009-06-15 17:04 sweetch[未注册用户]

@学习..

不是吧老大?你不看好WPF?那VISTA,WIN7是怎么来的?
 回复 引用   

#21楼 2009-06-15 17:14 学习..      

@sweetch
Vista, Win7 与WPF没有必然的联系啊,OS的核心部分还是Win32 API。和WPF关系都没有。
 回复 引用 查看   

#22楼 2009-06-15 18:18 neuhawk      

DataGrid不支持合并行,合并列.我打错标点符号了。  回复 引用 查看   

#23楼 2009-06-15 21:35 cokkiy

WPF这么有用的技术,怎么可能没用?只是你不熟悉罢了!等你熟悉了再来说,就会体现出它的强大和设计理念的先进.
提示一下:数据邦定从来不会直接邦定到控件上,而是用DataTemplate
 回复 引用   

#24楼 2009-06-15 21:37 cokkiy

竟然 说 html的table比Grid好用,真是无语。  回复 引用   

#25楼 2009-07-01 12:55 越学习越发现你的无知[未注册用户]

只用了这么短时间就来说三道四,这对于业内是不负责任的做法  回复 引用   

#26楼 2009-07-01 14:02 Yuanyi Wang      

不要用WinForm和Web的思考问题方法去思考WPF  回复 引用 查看   

#27楼 2009-08-19 17:05 麦上飞[未注册用户]

晕,我觉得这样设计grid不错啊。
如果手工删除html table某一列,很容易漏掉<td></td>,可能会引起布局异常乱,查找起来麻烦多了。
要兼顾的东西太多了,你说容易吗?
 回复 引用   

#28楼 2009-08-21 14:24 dujid[未注册用户]

各有千秋吧,相比grid,html的table有几个劣势:

1. html的table无法在一个grid中重叠放置对象,但Grid可以,只需要指定row和column,由于html的td内对于对象都是流式布局,所以默认是不支持。当然可以通过嵌套div,但是那样会增加复杂度;这点很重要,如果想支持重叠布局,那html的table的语法只会更复杂。而我在使用中也深刻的体会到,重叠布局简直是太好用了。

2. 想要移动html的table的对象,要比grid移动起来麻烦,不仅要顾及语法,还要从上往下数,现在是哪个一个tr哪一个td,但grid只需要改改数字;

3. 对于简单表格,html的table基本上还可以notepad编辑,但是遇到复杂报表,嵌套表格加上错综复杂的 rowspan 和 colspan,用notepad 来编辑简直就是噩梦,我不信有谁可以算得那么清楚,就算一时能算清楚,以后维护起来还是麻烦。但,grid只需要改改 rowspan 和 colspan 的数字;

可以看出来,wpf grid的设计者是吸取了table改善后的经验,其实,html的table才是更适合解析而不适合编辑。因为嵌套的tr td本身来说就是简单的 xml,但其不适合rowspan和colspan。多一层嵌套和多10层嵌套的解析难度,对于设计良好的程序来说几乎没有差别。



 回复 引用   

#29楼[楼主] 2009-08-21 16:45 Shuhari      

@麦上飞:
按照我的经验,只要1. 标签对齐没有错乱;2. td里面的内容不超过一整页,那么错误删除标签的可能性微乎其微,即使真的删错了,刷新一下页面马上就知道出问题了,没有什么麻烦的。

@dujid:
1. table在Web标准里面本来就不鼓励用来布局,多层嵌套的table更是应该绝对避免的,我也觉得重叠布局好,但没有理由要求table支持。
2. 我移动tr通常是看标签的内容来判断该移动到哪里,很少使用数数的方法。WPF的问题在于基于数字的方法“伸缩性”不好。不论多大的表格,移动只需要一次Ctrl+C/Ctrl+V,而修改数字的工作量可是随着表格的大小线性增长的。
3. 没有明白是什么意思。html table和WPF grid不是一样要修改colspan /rowspan么,差别在哪里?
 回复 引用 查看   

#30楼 2009-08-23 00:30 dujid[未注册用户]

reply 楼主:
1.我的意思是,Grid需要支持重叠布局,或者至少有这种需求,所以为了融合这样的需求,Grid需要在开始先定义rowdefines和coldefines,而在后面只需要指定index,这样可以很简单的做到重叠布局,而正因为html的table没有这样的需求,所以自然在语法上有不同。所以,在问题的一开始就不应该将两个设计需求不同的东西放在一起比较,如果你觉得grid不好,这种评价建立在你对于grid的需求之上,如果非要说不好的话,只能说在某种需求上某个设计有弊端,而这种弊端也是正常的,没有一个设计能做到完美无暇。在不同的需求前提下,每个人都会得到不同的答案,所以没有绝对的“糟糕”与“完美”。

2.在wpf里,一个单元格里放入很多对象的时候他们一定在一个panel对象里,而移动对象只需要改动这个panel的index,连ctrl+c/v都不需要了。

3.html的colspan标签,需要计算才能写对。比如有两行各有3列。
<tr>
<td/> <td/> <td/>
</tr>
<tr>
<td/> <td colspan="2"/>
</tr>
这样在标签上已经产生了麻烦,如果有5列,而在不同情况下,某一行将出现colspan,如果再加上rowspan呢?比如
<tr>
<td rowspan="2"> <td/>
</tr>
<tr>
<td/> <td colspan="2"/>
</tr>
你在大脑中能很快渲染出这个表格的结果么?实际上对于我来说,要判断这个表格在构造上是否正确也要想一想,这也只是简单的情况。实际在我所遇到的项目,更大更复杂的报表包含嵌套的表格和更多的colspan。这种情况也只有借助像dreamweaver这样的工具了。

其实我不仅不否认你的观点,我在使用了wpf两个月之后仍然认为wpf整体上是个“糟糕”的设计,或许我对wpf还有着一些误解,但出现在wpf里的种种概念,例如style, template, datatemplate, contenttemplate, binding,感觉有些概念很牵强,有一些设计看起来总是与期望不符,这与ms一直以来的设计理念也是有差别的。比如,如果需要用户去阅读说明书才能正确使用的软件,在人机交互方面的设计肯定是有问题的。好的软件的操作方法应该与用户的期望达成一致。而我为了使用wpf,不得不阅读msdn以外的资料,而且在使用中也处处碰到奇怪的问题。

我总认为wpf的设计者是在闭门造车,简单的来说databanding,对于简单的数据对象来说,手动更新要比data banding更简单和更有效,而对于复杂的binding来说,往往很多需求又不能实现,为了迎合这些需求,wpf里又出现了valueconverter, validation, collectionview 之类的概念来弥补,但是,这些设计有点学术,缺少适用性,如果参照java的很多framework来看有很多技术已经在别的领域被很好的实现了,wpf何不参考其并加以改善呢,例如binding的path这个属性,path不支持表达式,因此必须写valueconverter,而东一个converter西一个collectionview在设计上使得高内聚这样的设计目的打的很散,在需要修改设计时,就像你所描述的那样,需要同时修改几处地方,而这在问题的诊断上,也是大麻烦。为修改一个简单的设计而不得不一会打开xaml一会儿打开程序,还要在blend和vs两个ide下不停的切换,和reload工程文件,有时甚至出现回滚被覆盖。且不说blend和vs在稳定性上的问题。这种开发体验不得不说wpf在设计和整体规划上有“问题”。

.net3里同时装备了linq和wpf,但两者之间并没有必然的联系,甚至在某些功能上还有着设计重复,例如wpf的collectionview,排序和分组功能完全可以统一让linq来承担,而且那样效果会更好,甚至data banding这样的技术原本就应该让linq 来做,因为linq有一个好的功能就是对象类型转换,因此,这样的构架更让人觉得wpf更像是在闭门造车。其结果就是,有一些技术缺乏可靠的实用性研究。
 回复 引用   

#31楼[楼主] 2009-08-24 11:54 Shuhari      

@dujid:

我不知道你在什么情况下需要重叠布局,可能在这种场合下Grid有好处吧,但是对我的项目来说,80%以上的工作是表单布局,表单控件基本上是不会重叠的,而加一行或者减一行是经常的事情,这时候用Grid特别的麻烦。但是不用又不行,因为所有布局里面只有Grid支持按比例分配的布局方法。

你对WPF的看法我是同意的,我也觉得WPF在设计思想上出了问题,它没有设身处地为界面设计人员考虑怎样提供方便,而是挖空心思在一些所谓的灵活性上做文章,使得框架的可用性不佳。另一个问题是WPF太过于强调XAML了,总想什么都写在XAML里面,在实际应用的场合XAML很快变成一大锅Binding Soap。正如你说的,C#有很丰富的语法构造,用来表达一些概念比全部丢给XAML要方便得多。

 回复 引用 查看   

#32楼 2009-08-28 19:17 麦上飞[未注册用户]

@dujid:
1.不过valueconverter在嵌套绑定就非常有用了
<ListView MaxHeight="{Binding MaxHeight}" Margin="10,5,0,0" Name="lstFormat" ItemsSource="{Binding Formats}" ItemContainerStyle="{StaticResource alternatingListViewItemStyle}"
SelectedIndex="{Binding SelectedIndex}"
AlternationCount="2" SelectionMode="Single">
<ListView.View>
<GridView AllowsColumnReorder="true">
<GridViewColumn Width="Auto" Header="{DynamicResource Edit}">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Height="23">
<Hyperlink CommandParameter="{Binding}" TextDecorations="None">
<Hyperlink.Command>
<MultiBinding Converter="{StaticResource triggerConverter}">
<Binding Path="DataContext.EditCommand"
ElementName="lstFormat" />
<Binding />
</MultiBinding>
</Hyperlink.Command>
<Image Margin="0,3,0,0" Width="16" Height="16" Source="/Manroland;component/Images/Icon/button_edit.png" HorizontalAlignment="Center" VerticalAlignment="Center"></Image></Hyperlink>
</TextBlock>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
2.collectionview 在TabControl绑定实现中,
可以实现选定某个TabItem的焦点,
collectionView.MoveCurrentTo(workspace);
 回复 引用   

<2012年2月>
2930311234
567891011
12131415161718
19202122232425
26272829123
45678910

导航

统计

公告

昵称:Shuhari
园龄:2年8个月
粉丝:1
关注:0

搜索

 
 

常用链接

我的标签

随笔分类

随笔档案

相册

最新评论

阅读排行榜

评论排行榜

推荐排行榜