如何参数化WPF按钮的“内容”的子元素属性?

时间:2018-06-27 01:55:19

标签: wpf

我的应用程序(使用MahApps.Metro)具有许多<Button>的{​​{1}}元素,除了Content本身的不同属性以及我想通过<Button>上的新属性(附加属性?)来参数化单个Button.Content子元素属性。

我有大约20个此XAML实例用于按钮:

<Button>

我已经通过将常量属性移动到派生样式来缩小了它:

<Button
    Style="{StaticResource MetroCircleButtonStyle}"
    Margin="0,-4,-4,2"
    DockPanel.Dock="Right"
    Width="32"
    Height="32"
    Name="targetSystemAutoconfigureButton"
    ToolTip="{x:Static me:Resources.Settings_AutoconfigureTargetSystem}"
    Command="{Binding AutoconfigureTargetSystemCommand}"
>
    <Button.Content>
        <Rectangle Fill="{Binding Foreground, ElementName=targetSystemAutoconfigureButton}" Width="12" Height="12">
            <Rectangle.OpacityMask>
                <DrawingBrush Drawing="{StaticResource appbar_magnify}" Stretch="Uniform" />
            </Rectangle.OpacityMask>
        </Rectangle>
    </Button.Content>
</Button>

但是我只想拥有这个:

<!-- In a <ResourceDictionary> located elsewhere -->
<Style x:Key="inputSplitIconButton" TargetType="Button" BasedOn="{StaticResource MetroCircleButtonStyle}">
    <Setter Property="Margin" Value="0,-4,-4,2" />
    <Setter Property="DockPanel.Dock" Value="Right" />
    <Setter Property="Width" Value="32" />
    <Setter Property="Height" Value="32" />
</Style>

<!-- Button XAML is now: -->
<Button
    Style="{StaticResource inputSplitIconButton}"
    Name="targetSystemAutoconfigureButton"
    ToolTip="{x:Static me:Resources.Settings_AutoconfigureTargetSystem}"
    Command="{Binding AutoconfigureTargetSystemCommand}"
>
    <Button.Content>
        <Rectangle Fill="{Binding Foreground, ElementName=targetSystemAutoconfigureButton}" Width="12" Height="12">
            <Rectangle.OpacityMask>
                <DrawingBrush Drawing="{StaticResource appbar_magnify}" Stretch="Uniform" />
            </Rectangle.OpacityMask>
        </Rectangle>
    </Button.Content>
</Button>

WPF的一个问题是,至少有三种不同的方法可以达到相同的最终结果,但是我不知道WPF到底如何选择最好的。我知道我的选择是:

  • <Button Style="{StaticResource inputSplitIconButton}" Name="targetSystemAutoconfigureButton" ToolTip="{x:Static me:Resources.Settings_AutoconfigureTargetSystem}" Command="{Binding AutoconfigureTargetSystemCommand}" ImageMask="{StaticResource appbar_magnify}" <--- this property /> 中设置Content属性。
    • 但是如何参数化<Style x:Key="inputSplitIconButton">属性?
  • Drawing=""中设置DataTemplate属性,并为<Style x:Key="inputSplitIconButton">属性使用DataContext绑定,并将其作为新的Drawing=""传入按钮实例
    • 但这意味着我无法使用现有的绑定。
  • 在添加DataContext的过程中,存在不同的主题变化:
    • 使用附加属性在DataTemplate
    • 中设置属性
    • 滥用DataTemplate属性来存储Tag名称。
  • 子类StaticResource并在那里添加我自己的属性,并在代码中创建内容结构。
    • 这将是非常痛苦的,而且是非常惯用的WPF。
  • 定义附加属性(例如,“ Button”),该属性在元素上设置时会自动添加me:Drawing=""等子内容。
    • 这是“正确的”惯用的WPF吗?附加属性如何处理其所应用元素的标记?

2 个答案:

答案 0 :(得分:1)

我将使用您的按钮创建一个UserControl。

  • 所有按钮xaml移到UserControl中。
  • 任何需要动态设置的参数都将成为UserControl上的依赖项属性。

我将在此答案中添加示例代码,但我不确定它是否对您完全有益。希望现在您有了一个灯泡时刻,看看这是如何实现的。我已经多次采用这种模式来解决您要描述的问题,并且非常合适。

您甚至可以将ContentPresenter控件放入按钮的内容中,并将该控件的Content属性公开为另一个依赖属性,以提高绑定乐趣。绑定任何您喜欢的内容:更多文本,矩形等...或什么都不做。

随时发表评论,我很乐意对此进行充实...

答案 1 :(得分:0)

我通过使用DataTemplate,带有附加属性将图像名称设置为字符串,使用IValueConverter从字符串名称映射到Drawing对象资源来解决问题绑定到OpacityMask

感谢这些资源:

Resources.xaml:

<!-- Note the ordering of elements is important -->
<me:StringToStaticResourceConverter x:Key="ssr" />

<DataTemplate x:Key="inputSplitIconButtonContentTemplate">
    <Rectangle Fill="{Binding Foreground, RelativeSource={RelativeSource AncestorType=Button}}" Width="12" Height="12">
        <Rectangle.OpacityMask>
            <DrawingBrush Drawing="{Binding Path=(me:Buttons.Image), Mode=OneWay, Converter={StaticResource ssr}, RelativeSource={RelativeSource AncestorType=Button}}" Stretch="Uniform" />
        </Rectangle.OpacityMask>
    </Rectangle>
</DataTemplate>

<Style x:Key="inputSplitIconButton" TargetType="Button" BasedOn="{StaticResource MetroCircleButtonStyle}">
    <Setter Property="Margin" Value="0,-4,-4,2" />
    <Setter Property="DockPanel.Dock" Value="Right" />
    <Setter Property="Width" Value="32" />
    <Setter Property="Height" Value="32" />
    <Setter Property="TabIndex" Value="10" />
    <Setter Property="ContentTemplate" Value="{StaticResource inputSplitIconButtonContentTemplate}" />
</Style>

AttachedProperties.cs:

using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;

namespace MyProject
{
    public static class Buttons
    {
        public static readonly DependencyProperty ImageProperty = DependencyProperty.RegisterAttached
        (
            name           : "Image",
            propertyType   : typeof(String),
            ownerType      : typeof(Buttons),
            defaultMetadata: new FrameworkPropertyMetadata( defaultValue: null, flags: FrameworkPropertyMetadataOptions.AffectsRender )
        );

        public static void SetImage(UIElement element, String value)
        {
            element.SetValue( ImageProperty, value );
        }

        public static String GetImage(UIElement element)
        {
            return (String)element.GetValue( ImageProperty );
        }
    }

    public class StringToStaticResourceConverter : IValueConverter
    {
        public Object Convert(Object value, Type targetType, Object parameter, CultureInfo culture)
        {
            return Application.Current.FindResource( value );
        }

        public Object ConvertBack(Object value, Type targetType, Object parameter, CultureInfo culture)
        {
            return null;
        }
    }
}

Window.xaml:

<Button
    Style="{StaticResource inputSplitIconButton}"
    ToolTip="{x:Static me:Resources.Settings_BrowseFile}" 
    me:Buttons.Image="appbar_folder_ellipsis_drawing"
/>

它甚至也可以在WPF XAML设计器中使用!

我认为可以通过在{StaticResource appbar_folder_ellipsis_drawing}中使用Window.xaml并通过...时间来进行实验来简化!

更新:简体:

当我意识到StringToStaticResourceConverter也可以与附加属性一起使用时,我可以进一步简化解决方案并消除{StaticResource key}

  1. Resources.xaml中:
    1. 删除此行:<me:StringToStaticResourceConverter x:Key="ssr" />
    2. <DrawingBrush行变为:<DrawingBrush Drawing="{Binding Path=(me:Buttons.Image), Mode=OneWay, RelativeSource={RelativeSource AncestorType=Button}}" Stretch="Uniform" />
  2. Window.xaml中:
    1. 将附加的属性属性值更改为{StaticResource},如下所示:me:Buttons.Image="{StaticResource appbar_folder_ellipsis_drawing}"