代码之家  ›  专栏  ›  技术社区  ›  Dai

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

wpf
  •  2
  • Dai  · 技术社区  · 6 年前

    我的应用程序(使用 MahApps.Metro )有很多 <Button> 元素 Content 除了在 <按钮> 本身,以及一个 Button.Content 要通过新属性(附加属性?)参数化的子元素属性。上 <按钮> .

    对于一个按钮,我有大约20个此XAML实例:

    <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>
    

    但我想要的只是:

    <Button
        Style="{StaticResource inputSplitIconButton}"
        Name="targetSystemAutoconfigureButton"
        ToolTip="{x:Static me:Resources.Settings_AutoconfigureTargetSystem}"
        Command="{Binding AutoconfigureTargetSystemCommand}"
        ImageMask="{StaticResource appbar_magnify}"           <--- this property
    />
    

    WPF的一个问题是,至少有三种不同的方法可以实现相同的最终结果,但是我对WPF的了解还不足以在这一点上选择最好的方法。我知道我的选择是:

    • 设置 内容 中的属性 <Style x:Key="inputSplitIconButton"> .
      • 但是我如何参数化 Drawing="" 属性?
    • 设置 DataTemplate 中的属性 <style x:key=“输入拆分图标按钮”> 并使用 DataContext 绑定到 绘图=“” 属性,并将其作为新属性传入 数据上下文 对于那个按钮实例
      • 但这意味着我不能使用现有的绑定。
    • 沿着添加 数据模板 ,主题有多种多样性:
      • 使用附加属性在 数据模板
      • 滥用 Tag 属性来存储 StaticResource 姓名。
    • 子类 Button 在那里添加我自己的属性,并在代码中创建内容结构。
      • 这将是非常痛苦的,是非常非惯用的WPF。
    • 定义附加属性(“ me:Drawing="" “例如),当对元素进行设置时,会自动添加 <Rectangle>... 等子内容。
      • 这是“正确的”和惯用的WPF吗?附加属性如何操作其应用元素的标记?
    2 回复  |  直到 6 年前
        1
  •  1
  •   Lynn Crumbling    6 年前

    我会用你的按钮创建一个用户控件。

    • 您的所有按钮xaml都将移动到用户控件中。
    • 任何需要动态设置的参数都将成为用户控件的依赖属性。

    我会在这个答案中添加示例代码,但我不确定它是否会对您有好处。希望你现在有一个灯泡的时刻,看看这是怎么回事。我已经多次使用这种模式来精确地解决您描述的问题,它非常适合。

    甚至可以将ContentPresenter控件放到按钮的内容中,并公开该控件的 Content 属性作为另一个依赖属性,以满足您的绑定乐趣。将您喜欢的内容绑定到它:更多文本、矩形等…或者什么都没有。

    请随意发表评论,我很高兴把这件事做得更充实一些…

        2
  •  0
  •   Dai    6 年前

    我通过使用 DataTemplate ,附加属性以将图像名称设置为字符串, IValueConverter 从字符串名称映射到 Drawing 绑定到的对象资源 OpacityMask .

    感谢这些资源:

    资源.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>
    

    附件属性.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(窗口.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. 窗口.xaml 以下内容:
      1. 将附加属性属性值更改为 {StaticResource} 就像这样: me:Buttons.Image="{StaticResource appbar_folder_ellipsis_drawing}" .