WPF里最简单的控件的Style你能写对么?(默认Style是有问题的)

第一个问题,WPF最简单的常用控件是什么?(先声明Path不算控件)

 

无论从控件的功能还是从控件的外观来看,最简单都是那个在菜单和Ribbon里很常见的分隔线。没错,它已经从WinForm里的一个字符串(-)进化成一个独立控件(Separator)了。

 

Separator继承于Control,而且没有添加一个属性或事件。当仁不让地成为了WPF中最简单控件的首选。

 

其全部Style代码如下所示:

 

        <Style x:Key="DefaultSeparatorStyle" TargetType="{x:Type Separator}">

            <Setter Property="Background"

                    Value="{DynamicResource {x:Static SystemColors.ControlDarkBrushKey}}"/>

            <Setter Property="Margin" Value="0,2,0,2"/>

            <Setter Property="Focusable" Value="false"/>

            <Setter Property="Template">

                <Setter.Value>

                    <ControlTemplate TargetType="{x:Type Separator}">

                        <Border Height="1" SnapsToDevicePixels="true"

                                Background="{TemplateBinding Background}"

                                BorderBrush="{TemplateBinding BorderBrush}"

                                BorderThickness="{TemplateBinding BorderThickness}"/>

                    </ControlTemplate>

                </Setter.Value>

            </Setter>

        </Style>

 

这么简单的Style,还拿出来说什么?似乎也没有什么好学的啊。不就个TemplateBinding和绑定到SystemColors看上去还高深一些么?

 

好现在让你做个事情你就明白了。如果我们想要一个正方形的Button会怎么办?注意是Button,不是Separator

 

代码如下:

 

<Button Width="30" Height="30"/>

 

很简单,那么如果我要一个高度为2个像素的Separator怎么办?也一样写:

 

<Separator Height="2"/>

 

行么?

 

请再鄙视一眼那个默认Style,行么?如果可行,我就不写这篇文章了。

 

看到Style里的Border了么?第一个属性就是Height=1。这个属性NB啊,无论你Separator多高,我就是1个像素了。那如果就是想画一个2个像素高的Separator要怎么办啊?

 

重写Style!就是把默认Style里的Height=1去了,再用Setter加个默认高为1,再在想让Separator2个像素高的地方设置Height=2。万事大吉。要重写Style啊。

 

这篇文章主要不是来介绍如果写个高度为2Separator的。而是来强调Style的灵活性的。如果Style不够灵活,这次因为改Height就要改Style,下次是不是改颜色也要Style呢?还有BorderBrushBorderThicknessForegroundOpacityMask等等,是不是改个属性,就要新建一个Style呢?

 

肯定不行啊。所以在你写个Style的第一行的时候,就应该时刻惦记着,你在Style做的样子,Style的使用者是不是可以很方便改变这个Style的默认外观,做一些定制化。有时的确不能保证所有的Template内的控件都可以定制化,这时就要做权衡,让重要的控件的外观可定制化,次要的可以Hard CodeStyle内部。

 

所以在WPF控件的默认Style里,你可以看到很多TemplateBinding,这些Binding的存在就是为了不在TemplateHard Code控件的属性。

 

但是看到Separator的默认Style,我已经有些出离愤怒了。这可是打着Microsoft商标的微软特产啊,而且是其中技术含量最低的一个Style。结果却犯了最低级的错误,而且还不只一个。

 

错误如下:

1.       Hard Code高度为1

2.       Hard Code SnapsToDevicePixelsTure

3.       错误地使用了Border去绘制直线。应该使用PathLine。请不要为什么,谢谢。最基本的OOD了。即使不考虑逻辑,功能上也不正确的。

 

在此更提醒为数不多的WPF初学者们,学习要专注,可以沉迷于微软的技术,但不要迷信微软的技术。我也没有非难或是指摘微软员工的意思,Bug哪都有。找到BugFix或是Workaround才是主要的。

 

正确的Style有很多,下面一种供大家参考。

 

        <Style x:Key="CorrectSeparatorStyle" TargetType="{x:Type Separator}">

            <Setter Property="Background"

                    Value="{DynamicResource {x:Static SystemColors.ControlDarkBrushKey}}"/>

            <Setter Property="Margin" Value="0,2,0,2"/>

            <Setter Property="Focusable" Value="false"/>

            <Setter Property="Height" Value="1"/>

            <Setter Property="SnapsToDevicePixels" Value="True"/>

            <Setter Property="Template">

                <Setter.Value>

                    <ControlTemplate TargetType="{x:Type Separator}">

                        <Line Stretch="Fill" X2="1"

                              Stroke="{TemplateBinding Background}"

                              StrokeThickness="{TemplateBinding Height}"

                              StrokeStartLineCap="Square" StrokeEndLineCap="Square"/>

                    </ControlTemplate>

                </Setter.Value>

            </Setter>

        </Style>

 

解释一下:

1.       首先,这里的X2=”1”并不属于Hard Code

2.       StrokeStartLineCapStrokeEndLineCap属于Hard Code。但是不影响当然Separator的使用。Separator本身就没有这些属性让人设置的。

3.       HeightSnapsToDevicePixelSetter中设置默认值。

 

如果让我来写个Separator这个控件,我会把Line或是Path的一些有用的属性添加给Separator,就可以让Separator更加灵活。

 

在实际的项目中,如果的确需要一个强大的Separator控件,外观很灵活的,又不想写个单独的AdvSeparator控件。最简单的一个办法就是不用Separator,直接用Line,然后给Line定义一个SeparatorLineStyle。参考示例如下:

 

        <Style x:Key="SeparatorLineStyle"

               TargetType="{x:Type Line}">

            <Setter Property="X2" Value="1"/>

            <Setter Property="Height" Value="1"/>

            <Setter Property="Stretch" Value="Fill"/>

            <Setter Property="StrokeEndLineCap" Value="Square"/>

            <Setter Property="StrokeStartLineCap" Value="Square"/>

            <Setter Property="StrokeThickness"

                    Value="{Binding Height, RelativeSource={RelativeSource Self}}"/>

            <Setter Property="Stroke"

                    Value="{DynamicResource {x:Static SystemColors.ControlDarkBrushKey}}"/>

        </Style>

 

这样,既可以用到Line的所有高级功能去绘制一条Separator,又不需要自定义一个控件。

 

Style可以很强悍,也可以很破烂。

posted on 2010-03-05 22:14  南柯之石  阅读(5893)  评论(38编辑  收藏  举报

导航