我正在学习WPF中的控件模板,并了解如何使用自定义模板样式替换按钮外观。我看到要制作圆形按钮,必须使用相同的高度和宽度定义椭圆。
<Style x:Key="Button2" TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid>
<Ellipse Fill="LightGreen" Width="80" Height="80"/>
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="Control.Margin" Value="10"/>
</Style>
当然,这只会强制使用该样式的所有按钮都有一个直径为80像素的圆,无论按钮的大小如何。我希望圆圈能够采用较短的高度/宽度值,以便根据按钮大小调整动态缩放。
但是,我还没有阅读任何教导如何在纯XAML模板中完成此操作的材料?似乎需要一些代码隐藏来实现这种效果吗?
答案 0 :(得分:7)
这是TemplateBinding的用武之地(TemplateBinding在控件模板中使用,用于从模板化控件中检索值,在本例中为Button)。
<Ellipse Fill="LightGreen"
Width="{TemplateBinding ActualWidth}" Height="{TemplateBinding ActualHeight}"/>
请注意,这是一种较短的使用形式:
{Binding ActualWidth, RelativeSource={RelativeSource TemplatedParent}}
TemplateBinding标记扩展仅针对TemplatedParent绑定进行了优化。
那就是说,如果你想让它只是一个圆圈,如果你的椭圆是W / H中较小的一个,那么你的内容很容易流出它,我怀疑你真正想要的是什么......?我曾想过使用多值转换器来做到这一点,但是你无法绑定到转换器参数,所以就这样了。
在这种情况下,附加的行为会起作用,但它并不漂亮。
<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:c="clr-namespace:WpfApplication1"
xmlns:local="clr-namespace:WpfApplication1"
Title="Window1" Height="300" Width="300">
<Grid>
<Button Content="Yo!" Width="50" Height="30">
<Button.Template>
<ControlTemplate TargetType="Button">
<Grid>
<Ellipse Fill="LightGreen" local:ConstrainWidthHeight.ConstrainedWidth="{TemplateBinding ActualWidth}" local:ConstrainWidthHeight.ConstrainedHeight="{TemplateBinding ActualHeight}"/>
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</ControlTemplate>
</Button.Template>
</Button>
</Grid>
</Window>
......以及所附行为:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
namespace WpfApplication1 {
public class ConstrainWidthHeight {
public static readonly DependencyProperty ConstrainedWidthProperty =
DependencyProperty.RegisterAttached( "ConstrainedWidth", typeof( double ), typeof( ConstrainWidthHeight ), new PropertyMetadata( double.NaN, OnConstrainValuesChanged ) );
public static readonly DependencyProperty ConstrainedHeightProperty =
DependencyProperty.RegisterAttached( "ConstrainedHeight", typeof( double ), typeof( ConstrainWidthHeight ), new UIPropertyMetadata( double.NaN, OnConstrainValuesChanged ) );
public static double GetConstrainedHeight( FrameworkElement obj ) {
return (double) obj.GetValue( ConstrainedHeightProperty );
}
public static void SetConstrainedHeight( FrameworkElement obj, double value ) {
obj.SetValue( ConstrainedHeightProperty, value );
}
public static double GetConstrainedWidth( FrameworkElement obj ) {
return (double) obj.GetValue( ConstrainedWidthProperty );
}
public static void SetConstrainedWidth( FrameworkElement obj, double value ) {
obj.SetValue( ConstrainedWidthProperty, value );
}
private static void OnConstrainValuesChanged( object sender, DependencyPropertyChangedEventArgs e ) {
FrameworkElement element = sender as FrameworkElement;
if( element != null ) {
double width = GetConstrainedWidth( element );
double height = GetConstrainedHeight( element );
if( width != double.NaN && height != double.NaN ) {
double value = Math.Min( width, height );
element.Width = value;
element.Height = value;
}
}
}
}
}
好的,现在需要使用附加行为的原因(无论如何是AFAICT),是为了使椭圆居中(在非正方形/非圆形场景中),你需要HorizontalAlignment和VerticalAlignment能够生效。两者的默认值都是拉伸,当设置显式宽度/高度时,它的行为类似于中心。
当Stretch =“Uniform”打开时,你的椭圆将始终物理占据整个空间,它只是椭圆的绘制将被约束。使用此选项,您绘制的Ellipse图形将始终从左上角开始。因此,在这种情况下,如果您的按钮比较宽,那么椭圆的绘制部分将不会与文本一起居中。
此代码是您可能不想要的一个很好的例子:
<Ellipse Height="{TemplateBinding ActualHeight}" Width="{TemplateBinding ActualWidth}" Fill="LightGreen" Stretch="Uniform" />
...和使用它的按钮(非方形宽度/高度):
<Button Content="YO!" Style="{StaticResource Button2}" Width="120" Height="53" VerticalAlignment="Top"></Button>
看起来像这样:
Ugly http://www.freeimagehosting.net/uploads/84e62c4982.png
...与附加属性选项相比:
alt text http://www.freeimagehosting.net/uploads/40755babcd.png
答案 1 :(得分:1)
我自己就碰到了这个。
我有一个stretch=uniform
的椭圆对象来填充网格,但不会越界。这样可以正常工作,但是,如果网格不是完全正方形,则圆圈不会居中。
将椭圆放在视箱中解决了我的问题:
<Grid>
<Viewbox>
<Ellipse Stretch="Uniform"/>
</Viewbox>
</Grid>
这里唯一的问题是椭圆的基本尺寸是0(或1,不确定),这意味着使用stroke
将自动填充整个椭圆(笔划将设置在0上-size-ellipse,然后将调整椭圆的大小。)
中风:
笔划无效的解决方法是使用MinHeight / MinWidth作为椭圆:
<Grid Background="Yellow">
<Viewbox>
<Ellipse Style="{Binding Path=StyleStatus, ConverterParameter='CoilEllipse', Converter={StaticResource styleConverter}}"/>
</Viewbox>
</Grid>
使用以下样式:
<Style x:Key="BaseCoilEllipse" TargetType="Ellipse">
<Setter Property="MinHeight" Value="10" />
<Setter Property="MinWidth" Value="10" />
<!-- Because the ellipse is inside a viewbox, the stroke will be dependant on MinHight/MinWidth.
Basically the stroke will now be 4/10th of the ellipse (strokethickness*2 / MinHeight/MinWidth) -->
<Setter Property="StrokeThickness" Value="2"/>
<Setter Property="Stroke" Value="Green" />
<Setter Property="Stretch" Value="Uniform" />
<Setter Property="Fill" Value="Black"/>
</Style>
(请注意,StrokeThickness现在是 relative )
<强>结果:强>
答案 2 :(得分:0)
在Ellipse上设置Stretch = Uniform将自动像Min(Height,Width)一样工作。你不需要像Adam所说的那样附加属性。
<Style x:Key="Button2" TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid>
<Ellipse Height="{TemplateBinding Height}" Width="{TemplateBinding Width}" Fill="LightGreen" Stretch="Uniform"/>
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>