WPF自定义ListBox(二)

Helloj2ee的自定义ListBox(一),虽然很用心,但看出来反响不强烈。只能这一篇再给力一点。虽然评论很少,但是也看出一些讯息。

讯息之一,就是Helloj2eeWPF看不懂。的确在这里写的不是基本的东西,而是对其WPF的相关基础概念掌握了之后,才能看的一个系列。自定义控件在《葵花宝典——WPF自学手册》一书中,是放到第六卷——华山之巅,足见其高阶。因此Helloj2ee在后面列出的参考文献,就很有价值了。因为您若是看不懂,不妨去参考相关文献,即便您没有葵花宝典一书,但总可以知道需要掌握的相关概念是什么,参阅其他书又何妨?

讯息之二,是提供的源码无法打开,那么Helloj2ee已经更新过。如果解压还是失败,您不妨在下面的回帖中告诉我。

说到给力,那么只有让这个控件在本章当中酷起来或者炫起来,才是给力的王道。

 

CircularPanel的布局之美

 

给力之前,Helloj2ee还是得枯燥一下,CircularPanel的那几个参数。第一个参数是CircularPanel的初始角度(InitialAngle)。

 

 

剩下的参数在该图中可以展现。Align属性实质是指的ListBoxItem的旋转点,Left表示外接矩形的左上角点,Center表示外接矩形的中心点,Right表示外接矩形的右下角点。而Radius指的是从圆心到旋转点的半径。AngleItem则是ListBoxItem之间的角度差。如下图所示:

 

ListBoxItem的外观

ListBoxItem的外观也需要通过模板来自定义。注意观察下图。实际上这样一个ListBoxItem是由如下五个元素组成的,分别是一个Path,三个Rectangle和一个TextBlock组成的。可能有人要问,为什么是三个Rectangle,实际上只是为了让这个边框一层一层更有层次感。

 

好了,我们来看看代码。

 

代码
<Style TargetType="{x:Type ListBoxItem}">
            
<Setter Property="Template">
                
<Setter.Value>
                    
<ControlTemplate TargetType="{x:Type ListBoxItem}">
                        
<Grid x:Name="gridSwatch" Height="180" Background="#00000000" RenderTransformOrigin="0.447,0.88" Width="55" Cursor="Hand" >
                            
<Grid.RenderTransform>
                                
<TransformGroup>
                                    
<ScaleTransform />
                                    
<SkewTransform/>
                                    
<RotateTransform Angle="90"/>
                                    
<TranslateTransform X="-65.75" Y="-71.05"/>
                                
</TransformGroup>
                            
</Grid.RenderTransform>
                            
                            
<Path x:Name="overShape" Data="M0,3.0000024 C0,1.3431468 1.3431457,0 3,0 L47,0 C48.656853,0 50,1.3431468 50,3.0000024 L50,185.00012 C50,186.65698 48.656853,188.00014 47,188.00014 C30.666666,190.16705 26.666666,222.49997 24.666666,231.5 C22.833332,222.49997 14,187.83371 3,188.00014 C1.3431457,188.00014 0,186.65698 0,185.00012 z" Fill="#FF3C3C3C" Stroke="{x:Null}" Margin="0,-4,0,-47.5" IsHitTestVisible="False" Visibility="Collapsed"/>
                            
<Rectangle x:Name="shadow3" Margin="-4,-8,-4,-8" Fill="#FF000000" Stroke="{x:Null}" RadiusX="7" RadiusY="7" Opacity="0.08" IsHitTestVisible="False"/>
                            
<Rectangle x:Name="shadow2" Margin="-3,-7,-3,-7" Fill="#FF000000" Stroke="{x:Null}" RadiusX="6" RadiusY="6" Opacity="0.08" IsHitTestVisible="False"/>
                            
<Rectangle x:Name="shadow1" Margin="-2,-6,-2,-6" Fill="#FF000000" Stroke="{x:Null}" RadiusX="5" RadiusY="5" Opacity="0.08" IsHitTestVisible="False"/>
                            
<Rectangle x:Name="whiteSwatch" Margin="-1,-5,-1,-5" Fill="#FFFFFFFF" Stroke="{x:Null}" RadiusX="4" RadiusY="4" IsHitTestVisible="False"/>
                            
<TextBlock  FontSize="22" FontFamily="华文行楷" Text="{Binding XPath=ImageText}" >
                                
<TextBlock.RenderTransform>
                                    
<TransformGroup>
                                    
<RotateTransform Angle="90"/>
                                    
<TranslateTransform X="30" Y="30"/>
                                    
</TransformGroup>
                                    
</TextBlock.RenderTransform>
                            
</TextBlock>
                        
</Grid>
                    
</ControlTemplate>
                
</Setter.Value>
            
</Setter>
        
</Style>

 

 

 

上面的代码有几处,Helloj2ee简单说明一下。

1ListBoxItem自定义的模板,首先是一个Grid面板,该面板用了好几个变换,构成了一个变换组,尽管有诸如ScaleTransform等都未填写,但是这是为后面的动画埋下伏笔。

2)这样的Path,的确难以用VS2010绘制出来,那么使用Expression Blend绘制就好,此外IsHitTestVisible属性设置为False,表明它并不接受用户的输入,只是作为一个基本的图元构成,同时设置了该Path在平时并不可见。

3)注意TextBlock通过XPath属性设置了和XML文件的绑定。

好了再次运行程序,您看到了什么?

 

即使整个ListBox已经变样,但是它选择的效果可以说极其不好,因此我们必须再接再力,给它添加上一些动画效果。添加动画的效果主要靠控件模板的Trigger来触发了。大家先看代码,然后Helloj2ee再加以解释。

 

代码
<ControlTemplate.Triggers>
                            
<EventTrigger RoutedEvent="ListBoxItem.MouseEnter">
                                
<BeginStoryboard>
                                    
<Storyboard>
                                        
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="gridSwatch" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)">
                                            
<SplineDoubleKeyFrame KeySpline="0.5,0,0.5,1" KeyTime="00:00:00.1000000" Value="1.2"/>
                                        
</DoubleAnimationUsingKeyFrames>
                                        
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="gridSwatch" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)">
                                            
<SplineDoubleKeyFrame KeySpline="0.5,0,0.5,1" KeyTime="00:00:00.1000000" Value="1.2"/>
                                        
</DoubleAnimationUsingKeyFrames>
                                        
<ObjectAnimationUsingKeyFrames BeginTime="00:00:00" Duration="00:00:00.0010000" Storyboard.TargetName="overShape" Storyboard.TargetProperty="(UIElement.Visibility)">
                                            
<DiscreteObjectKeyFrame KeyTime="00:00:00">
                                                
<DiscreteObjectKeyFrame.Value>
                                                    
<Visibility>Visible</Visibility>
                                                
</DiscreteObjectKeyFrame.Value>
                                            
</DiscreteObjectKeyFrame>
                                        
</ObjectAnimationUsingKeyFrames>
                                    
</Storyboard>
                                
</BeginStoryboard>
                            
</EventTrigger>
                            
<EventTrigger RoutedEvent="ListBoxItem.MouseLeave">
                                
<BeginStoryboard>
                                    
<Storyboard>
                                        
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="gridSwatch" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)">
                                            
<SplineDoubleKeyFrame KeySpline="0.5,0,0.5,1" KeyTime="00:00:00.2000000" Value="1"/>
                                        
</DoubleAnimationUsingKeyFrames>
                                        
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="gridSwatch" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)">
                                            
<SplineDoubleKeyFrame KeySpline="0.5,0,0.5,1" KeyTime="00:00:00.2000000" Value="1"/>
                                        
</DoubleAnimationUsingKeyFrames>
                                        
<ObjectAnimationUsingKeyFrames BeginTime="00:00:00" Duration="00:00:00.0010000" Storyboard.TargetName="overShape" Storyboard.TargetProperty="(UIElement.Visibility)">
                                            
<DiscreteObjectKeyFrame KeyTime="00:00:00">
                                                
<DiscreteObjectKeyFrame.Value>
                                                    
<Visibility>Collapsed</Visibility>
                                                
</DiscreteObjectKeyFrame.Value>
                                            
</DiscreteObjectKeyFrame>
                                        
</ObjectAnimationUsingKeyFrames>
                                    
</Storyboard>
                                
</BeginStoryboard>
                            
</EventTrigger>
                           
                            
<Trigger Property="IsMouseOver"
                         Value
="True">
                                
<Setter Property="Panel.ZIndex"
                            Value
="1" />
                            
</Trigger>
                        
</ControlTemplate.Triggers>

 

 

主要是在MouseEnterMouseLeave事件里做文章,当鼠标滑过某个ListBoxItem,我们通过改变它的ScaleTransform,让它变大。而离开ListBoxItem再通过改变ScaleTransform,让它恢复原状。比如像这样的语法,就未必有很多人能够写出来。请大家留心。

 

Storyboard.TargetName="gridSwatch" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)"

 

 

 

光是变换还是不够的,因为当鼠标滑过的时候,很有可能希望这个ListBoxItem凸现出来,那么要用到的就是Panel.ZIndex这个附加属性,通过改变这个值,让鼠标滑过的ListBoxItem凸现出来。

好了,现在的ListBoxItem已经完全变样了,但是还有美中不足。最为美中不足的就是ListBoxItem选中的时候,会出现一个虚框,这样的视觉效果确实很不好。

 

 

解决之道,又是一句话。

 

<Setter Property="FocusVisualStyle" Value="{x:Null}" />

 

 

 

还有美中不足,比如选中某一个ListBoxItem,我们该作如何反应呢。这里helloj2ee就不再写这样的代码了。各位去尝试一下吧。

剩下的Helloj2ee需要做的是让这个对话框透明起来和加上中间的小圆点。中间的小圆点并不难加,让对话框完全透明也So Easy。那么Helloj2ee向各位奉上代码就好了。如果真的新手不知道该如何让不规则对话框透明的话,那么不妨参见葵花宝典第88.3.4节好了。

 源代码:/Files/helloj2ee/ListBoxDemo3.rar

 

 

 

 

 

 

posted @ 2010-12-09 00:31  helloj2ee  阅读(3335)  评论(3编辑  收藏  举报