按钮控制模板与可调整大小的圆圈

ice*_*ava 7 wpf styles button controltemplate

我正在学习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>
Run Code Online (Sandbox Code Playgroud)

当然,这只会强制使用该样式的所有按钮都有一个直径为80像素的圆,无论按钮的大小如何.我希望圆圈采用较短的高度/宽度值,以便它可以根据按钮大小调整动态缩放.

但是,我还没有阅读任何教导如何在纯XAML模板中完成此操作的材料?似乎需要一些代码隐藏来实现这种效果?

Ada*_*lls 7

这是TemplateBinding的用武之地(TemplateBinding在控件模板中使用,用于从模板化控件中检索值,在本例中为Button).

<Ellipse Fill="LightGreen" 
    Width="{TemplateBinding ActualWidth}" Height="{TemplateBinding ActualHeight}"/>
Run Code Online (Sandbox Code Playgroud)

请注意,这是一种较短的使用形式:

{Binding ActualWidth, RelativeSource={RelativeSource TemplatedParent}}
Run Code Online (Sandbox Code Playgroud)

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>
Run Code Online (Sandbox Code Playgroud)

......以及附加的行为:

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;
                }
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

好的,现在需要使用附加行为的原因(无论如何是AFAICT),是为了使椭圆居中(在非正方形/非圆形场景中),您需要使Horizo​​ntalAlignment和VerticalAlignment能够生效.两者的默认值都是Stretch,当设置了显式的Width/Height时,它的行为类似于Center.

使用Stretch ="Uniform"时,您的椭圆将始终物理占据整个空间,它只是椭圆的绘制将被约束​​.使用此选项,您绘制的Ellipse图形将始终从左上角开始.因此,在这种情况下,如果您的按钮比较宽,则椭圆的绘制部分将不会与文本一起居中.

这段代码是你可能不想要的一个很好的例子:

<Ellipse Height="{TemplateBinding ActualHeight}" Width="{TemplateBinding ActualWidth}" Fill="LightGreen" Stretch="Uniform" />
Run Code Online (Sandbox Code Playgroud)

...和使用它的按钮(非方形宽度/高度):

<Button Content="YO!" Style="{StaticResource Button2}" Width="120" Height="53" VerticalAlignment="Top"></Button>
Run Code Online (Sandbox Code Playgroud)

看起来像这样:

丑陋的http://www.freeimagehosting.net/uploads/84e62c4982.png

...与附加属性选项相比:

alt text http://www.freeimagehosting.net/uploads/40755babcd.png