可调整大小的圆形按钮控件模板

7

我正在学习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模板中实现此操作的材料?似乎需要一些代码才能实现这种效果?
3个回答

7
这就是TemplateBinding的作用(TemplateBinding在控件模板中使用,用于从模板化控件中检索值,例如Button)。
<Ellipse Fill="LightGreen" 
    Width="{TemplateBinding ActualWidth}" Height="{TemplateBinding ActualHeight}"/>

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

{Binding ActualWidth, RelativeSource={RelativeSource TemplatedParent}}

模板绑定标记扩展仅针对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;
                }
            }
        }
    }
}

好的,现在使用一个附加行为所需的原因是(据我所知),为了使椭圆居中(在非正方形/非圆形场景中),需要使HorizontalAlignment和VerticalAlignment生效。两者的默认值都为Stretch,当明确设置宽度/高度时,它的行为就像是居中。
在Stretch =“Uniform”时,您的椭圆形始终会真实地占用整个空间,只有椭圆形的绘制会受到限制。使用这种方式,您绘制的椭圆形图形始终从左上角开始。因此,在此情况下,如果您的按钮比高更宽,则椭圆形的绘制部分不会与文本一起居中。
此代码是您可能不需要的好示例:
<Ellipse Height="{TemplateBinding ActualHeight}" Width="{TemplateBinding ActualWidth}" Fill="LightGreen" Stretch="Uniform" />

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

<Button Content="YO!" Style="{StaticResource Button2}" Width="120" Height="53" VerticalAlignment="Top"></Button>

看起来像这样:

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

... 与使用附加属性选项相比,变成了这个样子:

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


好的,我已经尝试了这种方法,似乎将HorizontalAlignment="Center" VerticalAlignment="Center"应用于椭圆形也会使圆形在调整为大尺寸后消失。我想知道发生了什么事情。 - icelava
жӣҙж–°дәҶжҲ‘зҡ„зӯ”жЎҲпјҢдҪҝз”ЁдәҶActualWidthе’ҢActualHeightгҖӮз»‘е®ҡеҲ°W/HжҳҜеҜјиҮҙдҪ зңӢеҲ°зҡ„еҺҹеӣ гҖӮ - Adam Sills
更改为ActualWidth和ActualHeight后,它现在似乎呈现在中心。虽然我还没有完全理解为什么,但还是谢谢。 - icelava

1

我自己也遇到了这个问题。

我有一个椭圆对象,使用stretch=uniform来填充网格,但不超出边界。这样做还可以,但是如果网格不完全是正方形,圆圈就无法居中。

将椭圆放置在视图框中解决了我的问题:

<Grid>
    <Viewbox>
        <Ellipse Stretch="Uniform"/>
    </Viewbox>
</Grid>

这里唯一的问题是椭圆的基础大小为0(或1,不确定),这意味着使用stroke将自动填充整个椭圆(描边将设置在0大小的椭圆上,然后调整椭圆的大小)。 带有描边: 解决描边无法工作的方法是为椭圆使用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现在是相对的)

结果:

Centered ellipse with stroke


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>

这个格式是可行的,但当我将 HorizontalAlignment="Center" VerticalAlignment="Center" 添加到椭圆元素中以将其与 ContentPresenter 居中时,它会消失。有任何想法吗? - icelava

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接