wpf button控件模板

发布时间 2023-07-01 17:08:00作者: 薛定谔的小灯泡

1.从button的style说起

  button有很多属性,我们在xaml中定义一个按钮时可以指定button的content、background、height、width等等。这些都是button的属性,而style也不例外也是button的一个属性,只是在给style属性赋值时不能简单的像height=“100”一样简单的设定一个字符串。先看一个简单的例子

我们定义了一个style对象,名字叫btnstyle,然后给2个button的style属性上赋值这个btnstyle对象,这是一个最简单的方式用来给button的style属性赋值。其他控件也是如此。

btnstyle这个对象用targettype指定此style可以被谁使用,这里指定时button。内部把button的background设置成绿色,height设置成50,width设置成50。我们可以看到界面上2个button都被成功改变了。

1.1button的style的作用

如上面的例子,我们在style中可以改变button的一些属性。如background、height、width。在style中我们可以设置控件的各个属性。也可以设置一些trigger,做一些属性检测。比如当某个属性的值等于多少时,改变另一个属性的值是多少。

1.2style中的trigger

设置2个简单的trigger:

当button的content=“123”时,button的foreground变成红色

当button的content=“456”时,button的width变成200

 style的定义:

 运行结果:

 

2.进入controltemplate

2.1有了style为什么还需要有controltemplate

通过上面的例子我们可以看出如果我们想要改变button的属性,或者做一些trigger监听,可以使用button的style属性来完成。

现在来思考两个问题:

问题1:button为什么长这个样子

问题2:如何改变每个控件的外观

 

问题1:button为什么长这个样子?

为什么一个button默认是矩形,不是圆形,为什么button能显示一些文本?

答案就是:控件模板定义了button默认的样子。所以问题2也有了答案,改变控件的外观需要修改它的控件模板。(style是不能改变控件的外观的)

2.2button的控件模板初步体验

 控件模板是button的template属性,我们在资源中定义一个controltemplate,指定target为button,并写一个名字叫btnct。需要注意控件模板必须要指定key才行,另外为什么是controltemplate?因为template属性的类型是controlyemplate

 看btnct这个控件模板的效果:发现button不显示任何东西,图中红色方框中没显示任何东西。button的外观由控件模板决定,btnct这个模板是空的,所以导致不显示任何东西。

 我们需要在controltemplate标签内去定义button的外观,这里就完全由我们决定了,怎么写,他就怎么显示。

 例如:我们想让一个button的外观长这个样子

 好,这个按钮除了显示文本还能显示一个”x“,那我们开始布局。

运行结果确实显示了,但是感觉怪怪的,我们在进一步修改:添加一个背景色

 现在显示背景色了,再进一步优化:让按钮的样子变成圆形

 现在看我们已经改变了button的外观。

 修改控件模板只会改变外观不会影响事件触发

 到此为止我们已经通过修改控件模板成功改变的button的外观,这个圆形button除外观和原来的button不用,其他任何功能都一样,所有的事件都仍有效。

2.3templatebinding

 好,现在我们来看控件模板的代码

 <ControlTemplate TargetType="Button" x:Key="btnct">
            <Border  Background="Red" Height="50" Width="50" CornerRadius="25">
                <StackPanel Orientation="Horizontal" VerticalAlignment="Center">
                    <TextBlock Text="WPF"/>
                    <TextBlock Margin="10 0 0 0" Text="x"/>
                </StackPanel>
            </Border>
        </ControlTemplate>

我们设置background hight width都是再控件模板内部写的,现在来看一个问题,在定义button时给content height width赋值,发现button并没有随之变化,这是为什么?

 <Button  Content="templatetest" Height="100" Width="100"  Template="{StaticResource btnct}" Click="Button_Click"/>

因为我们在定义一个button时,wpf框架会从我们写的控件模板中解析这个button的外观,像content=”WPF“是写死在控件模板内部的,无论定义多少个button,content都显示WPF,那改如何解决这个问题?

很简单,只需要在button的content属性和控件模板内部的实际显示文本的控件产生一种关联即可。

我们看控件模板中真正显示WPF这个字符的是textblock

 我们不能在这里写死,假设我们定义10个button,每个button显示文本也不同,需要把每个button的content属性和控件模板内部的textblock关联起来,具体语法如下:

 

 可以看到button的content确实不一样了,同理我们在处理一下height和width

 结果如下:

 

总结一下:控件模板是一个模板,当我们定义button时,button的外观取决于控件模板怎么定义,而控件模板内部的某些数据,可能需要被定义的这个button传递进去,这个传递通道使用templatebinding完成

3.style和controlyemplate的关联

button可以同时设置style和controltemplate,用style改变button的属性,用controltemplate改变button的外观。

3.1style能不能设置控件模板内部的控件?

同时设置style和controltemplate

<Button  Content="333" Height="100" Width="100" Style="{StaticResource btnstyle}" Template="{StaticResource btnct}" Click="Button_Click"/>

style:

 controltemplate

 style中的property可以设置的属性都是targettype指定的类型的控件中的,那style能不能改变controltemplate中的border、stackpanel、textblock呢?

答案是:能改,但是不能直接改。

看下面这个图:button的style属性和template属性,style中能改controltemplate中的border的background吗?不能,虽然style中property可以设置background,但是这个background是由style中的targettype指定的,也就是button的background,并不是controltemplate中的border的。style中只能改button的background,把controltemplate中的border的background通过templatebinding到button的background。(注意:这里指的style是button的style)

 3.2控件模板的trigger

 控件模板中的trigger比style中的trigger一样,只多了一个targetname。控件模板中的trigger可以控件controltemplae标签内的所有对象,前提是设置一个name。(超出controltemplate标签的对象,无法被控制)

例如:鼠标悬浮,border变蓝色

 再例如:鼠标悬浮:border变浅蓝,文本变绿色,x变橙色

3.3 style的trigger和控件模板中的trigger的优先级

button的显示其实是由控件模板内部的border来显示的,我们设置button的height width background,最后都通过template binding设置到了border身上。

如果style和控件模板的trigger起了冲突,谁起作用?直接说结论:控件模板起作用。(可以理解为:哪个trigger距离被控对象越近,哪个trigger优先级越高)

 

4.逻辑树(logic tree)和视觉树(visual tree)

速记:

1.在window窗体上布局的控件组成的逻辑是逻辑树

2.逻辑树+控件的控件模板内部的控件=视觉树

3.视觉树包含逻辑树

5.ContentPresenter

button的content属性是一个object类型的对象,也就意味着,可以给content赋值任何对象,但在我们写的这个控件模板中,只能写字符串,因为控件模板内部使用了textblock。如果我给content放一个image对象,就显示不出来了,

 这时候需要ContentPresenter登场,只需要在控件模板内部放一个这个就可以了,它相当于一个占位符,我们传递什么,他就会显示什么

 

<button.content>默认可以省掉

 下面是另一个控件模板:(想说的是控件模板内部其实也是怎么布局的问题)

 

 

6.其他问题

6.1style是属性,template是属性。style中不可以再控制style,可以设置template

 不可以在style中再设置style

 可以在style中设置template

6.2全局引用控件模板

 style不屑key,则程序范围内的控件自动引用,控件模板不写key不行,写了key就得手动赋值,把控件模板写在style中即可则程序范围内的控件自动引用