【问题标题】:Template for button with icon and text带有图标和文本的按钮模板
【发布时间】:2020-08-30 20:44:40
【问题描述】:

我在为带有图标(从路径创建)和文本的按钮创建模板时遇到问题。我想在定义按钮时设置文本、图标和前景。我没有下面的工作代码。问题是如何定义绑定,然后如何设置值(图标、文本和前景、背景工作)。

模板:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <ControlTemplate x:Key="sciDefaultIconButton" TargetType="Button" >
        <Grid>
            <Rectangle x:Name="backgroundElement" Fill="{TemplateBinding Background}" RadiusX="5" RadiusY="5"/>
            <ContentPresenter ContentTemplate="{StaticResource ContentWithIcon}" HorizontalAlignment="Center" VerticalAlignment="Center" />
        </Grid>
    </ControlTemplate>

    <DataTemplate x:Key="ContentWithIcon">
        <DockPanel>
            <Path Data="{Binding Icon}" Margin="0,0,5,0" Fill="{Binding Foreground}" VerticalAlignment="Center"/>
            <TextBlock Text="{Binding Text}" VerticalAlignment="Center"/>
        </DockPanel>
    </DataTemplate>
</ResourceDictionary>

用法:

<UserControl x:Class="Scienion.Core.UI.Dialogs.LoginDialog"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
Height="195" Width="265">

    <Grid HorizontalAlignment="Left" Width="260">
        <Button x:Name="BtnExample" Template="{StaticResource sciDefaultIconButton}" Text="Hello" Icon="{StaticResource SomePath}" Foreground="Black" Background="White" HorizontalAlignment="Left" Margin="100,155,0,0" VerticalAlignment="Top" Width="120" Height="30">
        </Button>
    </Grid>
</UserControl>

提前谢谢你。

【问题讨论】:

  • 请注意,通过替换按钮的模板,您将失去 MouseOver、Pressed、Focused 等的所有默认视觉状态。这真的是您想要的吗?也许您只是想将 Button 的 Content 设置为 Path 和 TextBlock 的组合?
  • 还为按钮定义了自定义样式(也包含操作),但感谢您的警告。

标签: c# wpf xaml templates binding


【解决方案1】:
<Button Height="28" Click="RefreshGit" Margin="0,2">
    <StackPanel Orientation="Horizontal" HorizontalAlignment="Left" Width="80">
        <Image Source="Images/Refresh.png" Width="24"/>
        <Label>Refresh</Label>
    </StackPanel>
</Button>

如果您不想要 PNG 格式,可以使用如下所示的 SVG 字体:

<StackPanel Orientation="Horizontal" HorizontalAlignment="Stretch">
    <fa:ImageAwesome Icon="Github" Width="24" Height="24" Margin="0,0,4,0" />
    <TextBlock Padding="10,0">GIT</TextBlock>
</StackPanel>

【讨论】:

  • 这不是我想要的,因为模板中没有定义文本和图标。图标是使用 Path/SVG 定义的,如果图标(填充),我还需要更改颜色。
  • Images/Refresh.png ,这是你想要的吗?这不是路径?
  • 我需要在模板中使用绑定定义图标/文本,然后将模板应用于按钮并设置这两个变量(文本、图标)。图标被定义为静态资源:&lt;Geometry x:Key="StopIcon"&gt;M 0 0 h 10 v 10 h -10 Z&lt;/Geometry&gt;,我可以在模板中静态设置,但问题是使用绑定设置它。
  • 你不必嵌入 内联,你可以使用资源来做到这一点。但无论如何,这与这篇文章无关。
【解决方案2】:

我创建了一个控件来处理显示 Xaml 路径图标和关联的文本标题

public enum CaptionPosition { None, ToLeftOfIcon, AboveIcon, ToRightOfIcon, BelowIcon }

public enum IconSize { ExtraSmall, Small, Medium, Large, ExtraLarge, ExtraExtraLarge }

public class perXamlIconHost : Control
{
    static perXamlIconHost()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(perXamlIconHost), new FrameworkPropertyMetadata(typeof(perXamlIconHost)));
    }

    public FrameworkElement XamlIcon
    {
        get => (FrameworkElement)GetValue(XamlIconProperty);
        set => SetValue(XamlIconProperty, value);
    }

    public static readonly DependencyProperty XamlIconProperty =
        DependencyProperty.Register("XamlIcon", typeof(FrameworkElement), typeof(perXamlIconHost), new PropertyMetadata(null));

    public IconSize IconSize
    {
        get => (IconSize)GetValue(IconSizeProperty);
        set => SetValue(IconSizeProperty, value);
    }

    public static readonly DependencyProperty IconSizeProperty =
        DependencyProperty.Register("IconSize", typeof(IconSize), typeof(perXamlIconHost), new PropertyMetadata(IconSize.Medium));

    public string Caption
    {
        get => (string)GetValue(CaptionProperty);
        set => SetValue(CaptionProperty, value);
    }

    public static readonly DependencyProperty CaptionProperty =
        DependencyProperty.Register("Caption", typeof(string), typeof(perXamlIconHost), new PropertyMetadata(null));

    public CaptionPosition CaptionPosition
    {
        get => (CaptionPosition)GetValue(CaptionPositionProperty);
        set => SetValue(CaptionPositionProperty, value);
    }

    public static readonly DependencyProperty CaptionPositionProperty =
        DependencyProperty.Register("CaptionPosition", typeof(CaptionPosition), typeof(perXamlIconHost), new PropertyMetadata(CaptionPosition.ToRightOfIcon));

    public Brush StandardForeground
    {
        get => (Brush)GetValue(StandardForegroundProperty);
        set => SetValue(StandardForegroundProperty, value);
    }

    public static readonly DependencyProperty StandardForegroundProperty =
        DependencyProperty.Register("StandardForeground", typeof(Brush), typeof(perXamlIconHost), new PropertyMetadata(Brushes.Black));

    public Brush StandardHighlight
    {
        get => (Brush)GetValue(StandardHighlightProperty);
        set => SetValue(StandardHighlightProperty, value);
    }

    public static readonly DependencyProperty StandardHighlightProperty =
        DependencyProperty.Register("StandardHighlight", typeof(Brush), typeof(perXamlIconHost), new PropertyMetadata(Brushes.White));

    public Brush DisabledForeground
    {
        get => (Brush)GetValue(DisabledForegroundProperty);
        set => SetValue(DisabledForegroundProperty, value);
    }

    public static readonly DependencyProperty DisabledForegroundProperty =
        DependencyProperty.Register("DisabledForeground", typeof(Brush), typeof(perXamlIconHost), new PropertyMetadata(Brushes.Silver));

    public Brush DisabledHighlight
    {
        get => (Brush)GetValue(DisabledHighlightProperty);
        set => SetValue(DisabledHighlightProperty, value);
    }

    public static readonly DependencyProperty DisabledHighlightProperty =
        DependencyProperty.Register("DisabledHighlight", typeof(Brush), typeof(perXamlIconHost), new PropertyMetadata(Brushes.Gray));
}

// ==============================================================================================================================================

public class perXamlIconSizeConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        const int defaultSize = 40;

        if (!(value is IconSize))
            return defaultSize;

        var iconSizeValue = (IconSize)value;

        switch (iconSizeValue)
        {
            case IconSize.ExtraSmall:
                return defaultSize / 2;
            case IconSize.Small:
                return defaultSize * 3 / 4;
            case IconSize.Large:
                return defaultSize * 3 / 2;
            case IconSize.ExtraLarge:
                return defaultSize * 2;
            case IconSize.ExtraExtraLarge:
                return defaultSize * 5 / 2;
            default:
                return defaultSize;
        }
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

.

<Style TargetType="{x:Type ctrl:perXamlIconHost}">
    <Setter Property="Focusable" Value="False" />
    <Setter Property="HorizontalAlignment" Value="Center" />
    <Setter Property="Padding" Value="0" />

    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type ctrl:perXamlIconHost}">
                <Grid>
                    <Grid.Resources>
                        <ctrl:perXamlIconSizeConverter x:Key="IconSizeConverter" />
                    </Grid.Resources>

                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto" />
                        <RowDefinition Height="Auto" />
                        <RowDefinition Height="Auto" />
                    </Grid.RowDefinitions>

                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="Auto" />
                        <ColumnDefinition Width="Auto" />
                        <ColumnDefinition Width="Auto" />
                    </Grid.ColumnDefinitions>

                    <!--  Set the DataContext to "self" so that the Xaml Icon item can bind to the Foreground and BorderBrush properties  -->
                    <ContentControl
                        x:Name="PART_IconPresenter"
                        Grid.Row="1"
                        Grid.Column="1"
                        Width="{TemplateBinding IconSize,
                                                Converter={StaticResource IconSizeConverter}}"
                        Height="{TemplateBinding IconSize,
                                                 Converter={StaticResource IconSizeConverter}}"
                        HorizontalAlignment="Center"
                        VerticalAlignment="Center"
                        BorderBrush="{TemplateBinding StandardHighlight}"
                        Content="{TemplateBinding XamlIcon}"
                        DataContext="{Binding RelativeSource={RelativeSource Self}}"
                        Focusable="False"
                        Foreground="{TemplateBinding StandardForeground}" />

                    <TextBlock
                        x:Name="PART_CaptionTextBlock"
                        Grid.Row="1"
                        Grid.Column="2"
                        Margin="8,0,0,0"
                        HorizontalAlignment="Center"
                        VerticalAlignment="Center"
                        Foreground="{TemplateBinding StandardForeground}"
                        Text="{TemplateBinding Caption}" />
                </Grid>

                <ControlTemplate.Triggers>
                    <Trigger Property="IsEnabled" Value="False">
                        <Setter TargetName="PART_CaptionTextBlock" Property="Foreground" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=DisabledForeground}" />
                        <Setter TargetName="PART_IconPresenter" Property="BorderBrush" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=DisabledHighlight}" />
                        <Setter TargetName="PART_IconPresenter" Property="Foreground" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=DisabledForeground}" />
                    </Trigger>

                    <Trigger Property="CaptionPosition" Value="None">
                        <Setter TargetName="PART_CaptionTextBlock" Property="Visibility" Value="Collapsed" />
                    </Trigger>

                    <Trigger Property="CaptionPosition" Value="ToLeftOfIcon">
                        <Setter TargetName="PART_CaptionTextBlock" Property="Grid.Column" Value="0" />
                        <Setter TargetName="PART_CaptionTextBlock" Property="Grid.Row" Value="1" />
                        <Setter TargetName="PART_CaptionTextBlock" Property="Margin" Value="0,0,8,0" />
                    </Trigger>

                    <Trigger Property="CaptionPosition" Value="AboveIcon">
                        <Setter TargetName="PART_CaptionTextBlock" Property="Grid.Column" Value="1" />
                        <Setter TargetName="PART_CaptionTextBlock" Property="Grid.Row" Value="0" />
                        <Setter TargetName="PART_CaptionTextBlock" Property="Margin" Value="0,0,0,4" />
                    </Trigger>

                    <Trigger Property="CaptionPosition" Value="BelowIcon">
                        <Setter TargetName="PART_CaptionTextBlock" Property="Grid.Column" Value="1" />
                        <Setter TargetName="PART_CaptionTextBlock" Property="Grid.Row" Value="2" />
                        <Setter TargetName="PART_CaptionTextBlock" Property="Margin" Value="0,4,0,0" />
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
    <Setter Property="VerticalAlignment" Value="Center" />
</Style>

它支持两种颜色的图标,它们绑定到主机的 Foreground 和 BorderBrush 属性,并且还会改变颜色以指示禁用状态。

图标被定义为一种资源(注意每个数据值开头的一对移动项,以定义两个路径对象的公共坐标系)

<Grid
    x:Key="TestIcon"
    x:Shared="False"
    Background="Transparent">

    <Path
        Data="M 20,35 M 180,210 M 102.6,77.3 C 94.4,77.3 86.7,78.9 79.4,82.0 C 72.1,85.1 65.8,89.3 60.4,94.7 C 55.1,100.1 50.8,106.4 47.7,113.7 C 44.6,120.9 43.0,128.7 43.0,136.9 C 43.0,142.3 43.7,147.6 45.2,152.7 C 46.6,157.8 48.6,162.6 51.2,167.0 C 53.7,171.5 56.8,175.5 60.4,179.1 C 64.1,182.8 68.1,185.9 72.5,188.5 C 76.9,191.1 81.7,193.1 86.8,194.6 C 91.,196.0 97.1,196.7 102.6,196.7 C 108.1,196.7 113.3,196.0 118.4,194.6 C 123.4,193.1 128.1,191.1 132.6,188.5 C 137.0,185.9 141.0,182.8 144.6,179.1 C 148.2,175.5 151.3,171.5 154.0,167.0 C 156.6,162.6 158.6,157.8 160.0,152.7 C 161.4,147.6 162.2,142.3 162.2,136.9 C 162.2,131.4 161.4,126.1 160.0,121.0 C 158.6,116.0 156.6,111.2 154.0,106.8 C151.3,102.4 148.2,98.3 144.6,94.7 C 141.0,91.1 137.0,88.0 132.6,85.4 C 128.1,82.9 123.4,80.9 118.4,79.4 C 113.3,78.0 108.1,77.3 102.6,77.3 z                                                          M 102.6,60.3 C 109.6,60.3 116.4,61.2 122.9,63.0 C 129.5,64.9 135.6,67.4 141.3,70.8 C 146.9,74.1 152.1,78.2 156.8,82.9 C 161.5,87.5 165.5,92.7 168.8,98.4 C172.1,104.0 174.7,110.1 176.5,116.6 C 178.3,123.2 179.2,129.9 179.2,136.9 C 179.2,143.9 178.3,150.7 176.5,157.2 C 174.7,163.8 172.1,169.9 168.8,175.5 C165.5,181.2 161.5,186.4 156.8,191.1 C 152.1,195.7 146.9,199.7 141.3,203.1 C 135.6,206.4 129.5,209.0 122.9,210.8 C 116.4,212.6 109.6,213.5 102.6,213.5 C 95.6,213.5 88.8,212.6 82.2,210.8 C 75.7,209.0 69.6,206.4 63.9,203.1 C 58.3,199.7 53.1,195.7 48.4,191.1 C 43.7,186.42 39.7,181.2 36.4,175.5 C 33.1,169.9 30.5,163.8 28.7,157.2 C 26.8,150.7 25.9,143.9 25.9,136.9 C 25.9,129.9 26.8,123.2 28.7,116.6 C 30.5,110.1 33.1,104.0 36.4,98.4 C 39.7,92.7 43.7,87.5 48.4,82.9 C 53.1,78.2 58.3,74.1 63.9,70.8 C 69.6,67.4 75.7,64.9 82.2,63.0 C 88.8,61.2 95.6,60.3 102.6,60.3 z"
        Fill="{Binding Foreground, FallbackValue=Cyan}"
        Stretch="Fill" />

    <Path
        Data="M 20,35 M 180,210 M 102.6,89.1 C 104.9,89.1 106.8,89.9 108.3,91.5 C 109.7,93.1 110.5,95.1 110.5,97.5 L 110.5,128.5 L 135.7,128.5 C 138.2,128.5 140.2,129.3 141.8,131.0 C 143.4,132.6 144.2,134.6 144.2,136.9 C 144.2,139.3 143.4,141.4 141.8,143.1 C 140.2,144.9 138.2,145.7 135.7,145.7 L 102.6,145.7 C 100.2,145.7 98.2,144.9 96.6,143.1 C 95.0,141.4 94.2,139.3 94.2,136.9 L 94.2,97.5 C 94.2,95.1 95.0,93.1 96.6,91.5 C 98.2,89.9 100.2,89.1 102.6,89.1 z                                                          M 142.0,35.9 C 145.2,35.9 148.6,36.5 152.0,37.6 C 155.5,38.7 159.1,40.6 162.8,43.2 C 168.6,47.2 172.8,51.4 175.3,56.0 C 177.8,60.5 179.1,65.1 179.1,69.8 C 179.1,72.7 178.6,75.6 177.8,78.5 C 176.9,81.3 175.7,84.2 174.2,87.0 C 170.8,82.2 167.1,77.8 162.9,73.8 C 158.7,69.7 154.1,66.2 149.2,63.1 C 144.3,60.0 139.1,57.4 133.5,55.3 C 127.9,53.2 122.2,51.6 116.3,50.6 C 119.6,46.1 123.5,42.6 127.8,39.9 C 132.1,37.2 136.8,35.9 142.0,35.9 z                                                     M 58.7,35.8 C 64.0,35.8 68.8,37.2 73.2,40.0 C 77.6,42.9 81.5,46.6 84.8,51.3 C 79.0,52.5 73.3,54.3 67.9,56.7 C 62.4,59.0 57.3,61.9 52.5,65.2 C 47.8,68.6 43.3,72.4 39.3,76.6 C 35.3,80.8 31.7,85.3 28.6,90.3 C 26.3,87.0 24.6,83.7 23.4,80.3 C 22.2,76.8 21.6,73.4 21.6,69.9 C21.6,65.1 22.9,60.5 25.4,56.0 C 27.9,51.4 32.0,47.2 37.7,43.3 C 41.5,40.7 45.1,38.8 48.6,37.6 C 52.1,36.4 55.5,35.8 58.7,35.8 z"
        Fill="{Binding BorderBrush, FallbackValue=Magenta}"
        Stretch="Fill" />
</Grid>

并像这样使用

<Button Grid.Row="1">
    <vctrl:perXamlIconHost
        Caption="Small / Below"
        CaptionPosition="BelowIcon"
        IconSize="Small"
        XamlIcon="{StaticResource TestIcon}" />
</Button>

同一个xaml路径资源可以通过改变IconSize属性产生不同大小的图标。

关于我的blog post的更多详细信息。

【讨论】:

  • 谢谢,这看起来不错。这是相当先进的。我想知道你是否知道如何使用 DataTemplate 来做到这一点(只有简单的没有图标大小和阳离子位置)。唯一的问题是如何将绑定从数据模板传播到按钮本身。
猜你喜欢
  • 2011-02-13
  • 2016-09-03
  • 2014-05-22
  • 2018-09-01
  • 2019-10-27
  • 1970-01-01
  • 2023-01-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多