在附加属性

时间:2017-08-04 18:42:09

标签: c# wpf xaml data-binding wpf-style

我试图将一些定义 MenuItem 的XAML移动到样式

我有以下工作XAML:

<Menu x:Name="menu" Height="19" Margin="10,10,10.333,0" VerticalAlignment="Top">
        <MenuItem Header="_Open">
            <MenuItem.Icon>
                <Viewbox>
                    <ContentControl Content="{DynamicResource appbar.folder.open}" RenderTransformOrigin="0.5,0.5">
                        <ContentControl.RenderTransform>
                            <TransformGroup>
                                <ScaleTransform ScaleX="2" ScaleY="2"/>
                            </TransformGroup>
                        </ContentControl.RenderTransform>
                    </ContentControl>
                </Viewbox>
            </MenuItem.Icon>
        </MenuItem>
</Menu>

资源如下所示:

<?xml version="1.0" encoding="utf-8"?>
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Canvas xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
            x:Key="appbar.folder.open" Width="76" Height="76" Clip="F1 M 0,0L 76,0L 76,76L 0,76L 0,0">
        <Path Width="44" Height="26" Canvas.Left="19" Canvas.Top="24" Stretch="Fill" Fill="#FF000000" Data="F1 M 19,50L 28,34L 63,34L 54,50L 19,50 Z M 19,28.0001L 35,28C 36,25 37.4999,24.0001 37.4999,24.0001L 48.75,24C 49.3023,24 50,24.6977 50,25.25L 50,28L 53.9999,28.0001L 53.9999,32L 27,32L 19,46.4L 19,28.0001 Z "/>
    </Canvas>
</ResourceDictionary>

作为旁注,我也不知道如何摆脱明确的缩放。这似乎是一个小问题,但如果这也可以解决,我将不胜感激。

无论如何,为了尽可能地将这个有点过于表达的定义移动到Style中,我为Visual类型的附加属性创建了代码

namespace extensions
{
    public class AttachedProperties
    {
        public static readonly DependencyProperty VisualIconProperty =
            DependencyProperty.RegisterAttached("VisualIcon",
                typeof(System.Windows.Media.Visual), typeof(AttachedProperties),
                new PropertyMetadata(default(System.Windows.Media.Visual)));

        public static void SetVisualIcon(UIElement element, System.Windows.Media.Visual value)
        {
            element.SetValue(VisualIconProperty, value);
        }
        public static System.Windows.Media.Visual GetVisualIcon(UIElement element)
        {
            return (System.Windows.Media.Visual)element.GetValue(VisualIconProperty);
        }
    }
}

重新定义了菜单项

<MenuItem Header="_Open"
          Style="{StaticResource MenuItemStyle}"
          extensions:AttachedProperties.VisualIcon="{DynamicResource appbar.folder.open}" />

并尝试创建一个Style,它读取VisualIcon属性以设置Icon内容

<Style x:Key="MenuItemStyle" TargetType="MenuItem">
    <Setter Property="MenuItem.Icon">
        <Setter.Value>
            <Viewbox>
                <ContentControl RenderTransformOrigin="0.5,0.5">
                    <ContentControl.Content>
                        <!-- this would work but doesn't reference the VisualIcon property.. <DynamicResource ResourceKey="appbar.folder.open"/> -->
                        <!-- these do not -->
                        <Binding Path="(extensions:AttachedProperties.VisualIcon)" RelativeSource="{RelativeSource FindAncestor, AncestorType=MenuItem}"/>-->
                        <!--<Binding Path="(extensions:AttachedProperties.VisualIcon)" RelativeSource="{RelativeSource TemplatedParent}"/>-->
                        <!--<Binding Path="(extensions:AttachedProperties.VisualIcon)" RelativeSource="{RelativeSource Self}"/>-->
                    </ContentControl.Content>
                    <ContentControl.RenderTransform>
                        <TransformGroup>
                            <ScaleTransform ScaleX="2" ScaleY="2"/>
                        </TransformGroup>
                    </ContentControl.RenderTransform>
                </ContentControl>
            </Viewbox>
        </Setter.Value>
    </Setter>
</Style>

但失败了。使用DynamicResource和静态密钥引用资源有效,但我无法使用附加属性进行任何绑定。

由于我是WPF初学者,我不确定这是否是一个好方法。

[EDIT1]

我测试了所有提供的解决方案。 Grek40 answer在设计视图和运行时都没有为我显示任何图标;也许我做错了。

grx70 second answer中的第二种方法对我来说最有希望,因为它可以在设计视图中以及在运行时间内可靠地显示图标。 但是,似乎Win7和Win10之间存在差异我不明白。我在Win7和Win10上测试了相同的源(在外部驱动器上)。对于Win10来说,它看起来不错,但是Win7的图标太大了。

(注意:原因在于this comment

Difference Win7/Win10

这是窗口测试代码:

<Window.Resources>
    <Style x:Key="MenuItemStyle" TargetType="controls:MenuItemEx">
        <Setter Property="MenuItem.Icon">
            <Setter.Value>
                <Viewbox>
                    <ContentControl RenderTransformOrigin="0.5,0.5">
                        <ContentControl.Content>
                            <Binding Path="(extensions:AttachedProperties.VisualIcon)" RelativeSource="{RelativeSource FindAncestor, AncestorType=MenuItem}"/>
                        </ContentControl.Content>
                        <ContentControl.RenderTransform>
                            <TransformGroup>
                                <ScaleTransform ScaleX="2" ScaleY="2"/>
                            </TransformGroup>
                        </ContentControl.RenderTransform>
                    </ContentControl>
                </Viewbox>
            </Setter.Value>
        </Setter>
    </Style>
    <Style x:Key="MenuItemStyle2" TargetType="MenuItem">
        <Setter Property="uihelpers:MenuItemHelper.IsEnabled" Value="True" />
        <Setter Property="MenuItem.Icon">
            <Setter.Value>
                <Viewbox>
                    <ContentControl RenderTransformOrigin="0.5,0.5"
                        Content="{Binding
                        Path=(uihelpers:MenuItemHelper.MenuItem).(extensions:AttachedProperties.VisualIcon),
                        RelativeSource={RelativeSource AncestorType=Viewbox}}">
                        <ContentControl.RenderTransform>
                            <TransformGroup>
                                <ScaleTransform ScaleX="2" ScaleY="2"/>
                            </TransformGroup>
                        </ContentControl.RenderTransform>
                    </ContentControl>
                </Viewbox>
            </Setter.Value>
        </Setter>
    </Style>

    <Style x:Key="MenuItemStyle3" TargetType="{x:Type MenuItem}">
        <Style.Resources>
            <DataTemplate x:Key="MenuItemStyle3dt" DataType="{x:Type Style}">
                <Path Style="{Binding}"
                      Stretch="Uniform"
                      HorizontalAlignment="Center"
                      VerticalAlignment="Center" />
            </DataTemplate>
        </Style.Resources>
    </Style>
    <Style x:Key="appbar.folder.open.style3.local" TargetType="{x:Type Path}">
        <Setter Property="Fill" Value="Black" />
        <Setter Property="Data" Value="M0,26 L9,10 L44,10 L35,26 Z M0,4 L16,4 C17,1 18.5,0 18.5,0 L29.75,0 C30.3,0 31,0.7 31,1.25 L31,4 L34,4 L34,8 L8,8 L0,22.4 Z" />
    </Style>
    <extensions:PathStyle x:Key="appbar.folder.open.style3b">
        <Setter Property="Path.HorizontalAlignment" Value="Center" />
        <Setter Property="Path.VerticalAlignment" Value="Center" />
        <Setter Property="Path.Stretch" Value="Uniform" />
        <Setter Property="Path.Fill" Value="Black" />
        <Setter Property="Path.Data" Value="M0,26 L9,10 L44,10 L35,26 Z M0,4 L16,4 C17,1 18.5,0 18.5,0 L29.75,0 C30.3,0 31,0.7 31,1.25 L31,4 L34,4 L34,8 L8,8 L0,22.4 Z" />
    </extensions:PathStyle>
    <DataTemplate DataType="{x:Type extensions:PathStyle}">
        <Path Style="{Binding}" />
    </DataTemplate>

    <Canvas x:Key="appbar.folder.open.style4.local" x:Shared="False" Width="76" Height="76" Clip="F1 M 0,0L 76,0L 76,76L 0,76L 0,0">
        <Path Width="44" Height="26" Canvas.Left="19" Canvas.Top="24" Stretch="Fill" Fill="#FF000000" Data="F1 M 19,50L 28,34L 63,34L 54,50L 19,50 Z M 19,28.0001L 35,28C 36,25 37.4999,24.0001 37.4999,24.0001L 48.75,24C 49.3023,24 50,24.6977 50,25.25L 50,28L 53.9999,28.0001L 53.9999,32L 27,32L 19,46.4L 19,28.0001 Z "/>
    </Canvas>
    <Viewbox x:Key="MenuItemStyle4.Icon" x:Shared="False">
        <ContentControl Content="{Binding Path=Tag,RelativeSource={RelativeSource AncestorType=MenuItem}}" RenderTransformOrigin="0.5,0.5">
            <ContentControl.RenderTransform>
                <TransformGroup>
                    <ScaleTransform ScaleX="2" ScaleY="2"/>
                </TransformGroup>
            </ContentControl.RenderTransform>
        </ContentControl>
    </Viewbox>
    <Style x:Key="MenuItemStyle4" TargetType="MenuItem">
        <Setter Property="Icon" Value="{StaticResource MenuItemStyle4.Icon}"/>
    </Style>
</Window.Resources>
<Grid>
    <Menu x:Name="menu" Height="19" Margin="10,10,10.333,0" VerticalAlignment="Top">
        <MenuItem Header="_File">
            <controls:MenuItemEx Header="_Open"
                      Style="{StaticResource MenuItemStyle}"
                      extensions:AttachedProperties.VisualIcon="{DynamicResource appbar.folder.open}" />
            <MenuItem Header="_Open"
                      Style="{StaticResource MenuItemStyle2}"
                      extensions:AttachedProperties.VisualIcon="{DynamicResource appbar.folder.open}" />
            <MenuItem Header="_Open"
                      Style="{StaticResource MenuItemStyle3}"
                      Icon="{DynamicResource appbar.folder.open.style3}" />
            <MenuItem Header="_Open"
                      Style="{StaticResource MenuItemStyle3}"
                      Icon="{DynamicResource appbar.folder.open.style3.local}" />
            <MenuItem Header="_Open" Icon="{DynamicResource appbar.folder.open.style3b}" />
            <MenuItem Header="_Open"
                      Style="{StaticResource MenuItemStyle4}"
                      Tag="{DynamicResource appbar.folder.open}" />
            <MenuItem Header="_Open"
                      Style="{StaticResource MenuItemStyle4}"
                      Tag="{DynamicResource appbar.folder.open.style4.local}" />
            <MenuItem Header="_Save">
                <MenuItem.Icon>
                    <Viewbox>
                        <ContentControl Content="{DynamicResource appbar.save}" RenderTransformOrigin="0.5,0.5">
                            <ContentControl.RenderTransform>
                                <TransformGroup>
                                    <ScaleTransform ScaleX="2" ScaleY="2"/>
                                </TransformGroup>
                            </ContentControl.RenderTransform>
                        </ContentControl>
                    </Viewbox>
                </MenuItem.Icon>
            </MenuItem>
        </MenuItem>
    </Menu>
    <Menu x:Name="menu2" Height="19" Margin="9,32,11.333,0" VerticalAlignment="Top" ItemContainerStyle="{StaticResource MenuItemStyle4}">
        <MenuItem Header="_File">
            <MenuItem Header="_Open"
                       Tag="{DynamicResource appbar.folder.open.style4.local}" />
            <MenuItem Header="_Open"
                       Tag="{DynamicResource appbar.folder.open}" />
        </MenuItem>
    </Menu>
</Grid>

以下是全球资源:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Canvas xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
            x:Key="appbar.folder.open" x:Shared="False" Width="76" Height="76" Clip="F1 M 0,0L 76,0L 76,76L 0,76L 0,0">
        <Path Width="44" Height="26" Canvas.Left="19" Canvas.Top="24" Stretch="Fill" Fill="#FF000000" Data="F1 M 19,50L 28,34L 63,34L 54,50L 19,50 Z M 19,28.0001L 35,28C 36,25 37.4999,24.0001 37.4999,24.0001L 48.75,24C 49.3023,24 50,24.6977 50,25.25L 50,28L 53.9999,28.0001L 53.9999,32L 27,32L 19,46.4L 19,28.0001 Z "/>
    </Canvas>
    <Style x:Key="appbar.folder.open.style3" TargetType="{x:Type Path}">
        <Setter Property="Fill" Value="Black" />
        <Setter Property="Data" Value="M0,26 L9,10 L44,10 L35,26 Z M0,4 L16,4 C17,1 18.5,0 18.5,0 L29.75,0 C30.3,0 31,0.7 31,1.25 L31,4 L34,4 L34,8 L8,8 L0,22.4 Z" />
    </Style>
</ResourceDictionary>

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Canvas xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
            x:Key="appbar.save" Width="76" Height="76" Clip="F1 M 0,0L 76,0L 76,76L 0,76L 0,0">
        <Path Width="34.8333" Height="34.8333" Canvas.Left="20.5833" Canvas.Top="20.5833" Stretch="Fill" Fill="#FF000000" Data="F1 M 20.5833,20.5833L 55.4167,20.5833L 55.4167,55.4167L 45.9167,55.4167L 45.9167,44.3333L 30.0833,44.3333L 30.0833,55.4167L 20.5833,55.4167L 20.5833,20.5833 Z M 33.25,55.4167L 33.25,50.6667L 39.5833,50.6667L 39.5833,55.4167L 33.25,55.4167 Z M 26.9167,23.75L 26.9167,33.25L 49.0833,33.25L 49.0833,23.75L 26.9167,23.75 Z "/>
    </Canvas>
</ResourceDictionary>

在app.xaml中合并:

<Application.Resources>
    <ResourceDictionary >
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="icons/appbar.folder.open.xaml"/>
            <ResourceDictionary Source="icons/appbar.save.xaml"/>
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
</Application.Resources>

图标被偏移的原因是我从github.com/Templarian/WindowsIcons或多或少1:1获取它们并且希望因为它们以这种格式提供(我也试图将它们保存为画笔,首先使用Inkscape,然后使用Expression Design),通常会像这样使用它们。

3 个答案:

答案 0 :(得分:1)

诊断

绑定不起作用的原因如下:

  1. 对于RelativeSourceMode.Self - 因为未在VisualIcon上明确设置ContentControl附加属性,其默认值为null
  2. 对于RelativeSourceMode.TemplatedParent - 因为ContentControl不是模板的一部分(如果是,从您提供的代码中看不出来,可能是VisualIcon的值对于模板化的父级是null
  3. 对于RelativeSourceMode.FindAncestor - 因为MenuItem没有将MenuItem.Icon属性的值设置为其逻辑子项,所以在将ContentControl加载到可视树中之前,无法建立两者之间的关系。并且,显然,在这种情况下,绑定在此之前得到解决,并且由于某种原因在最终加载ContentControl时没有重新解析。
  4. 将(S)

    您可以采取几种方法来解决此问题,以下是其中一些方法。

    <强>予。仅在加载ContentControl

    时设置绑定

    您可以将以下处理程序订阅到ContentControl.Loaded事件:

    private void ContentControl_Loaded(object sender, RoutedEventArgs e)
    {
        var control = (ContentControl)sender;
        control.SetBinding(ContentControl.ContentProperty, new Binding
        {
            Path = new PropertyPath(AttachedProperties.VisualIconProperty),
            RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor)
            {
                AncestorType = typeof(MenuItem),
            },
        });
    }
    

    然后在 XAML

    <ContentControl
        Content="{Binding
            Path=(extensions:AttachedProperties.VisualIcon),
            RelativeSource={RelativeSource AncestorType=MenuItem}}"
        Loaded="ContentControl_Loaded" (...)>
        (...)
    </ContentControl>
    

    请注意,如果将样式放在单独文件的资源字典中,则必须按照these instructions进行操作。

    您还可以创建自定义MarkupExtension(例如,名为DeferredBinding)来完成这项工作。

    <强> II。子类MenuItem并将图标设置为逻辑子

    写的代码不多:

    public class MyMenuItem : MenuItem
    {
        protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
        {
            base.OnPropertyChanged(e);
            if (e.Property == IconProperty)
            {
                if (e.OldValue != null)
                    RemoveLogicalChild(e.OldValue);
                if (e.NewValue != null)
                    AddLogicalChild(e.NewValue);
            }
        }
    }
    

    但缺点是你总是要记住使用MyMenuItem代替MenuItem

    <local:MyMenuItem Header="_Open"
          Style="{StaticResource MenuItemStyle}"
          extensions:AttachedProperties.VisualIcon="{DynamicResource appbar.folder.open}" />
    

    <Style x:Key="MenuItemStyle" TargetType="local:MyMenuItem">
        (...)
    </Style>
    

    在这种情况下,您还可以FindAncestor模式进行绑定。

    <强> III。创建一个帮助程序类,允许通过附加属性访问MenuItem中的相关ContentControl

    以下帮助器类包含两个依赖项属性 - IsEnabled(我相信它是自我解释的)和MenuItem,它是只读的并保存实际的MenuItem,其中目标图标已设置:

    public static class MenuItemHelper
    {
        /**** Here are the important parts: ****/
    
        //When IsEnabled changes we need to either hook things up or do the cleanup
        private static void HandleIsEnabledChanged(
            DependencyObject d,
            DependencyPropertyChangedEventArgs e)
        {
            var item = (MenuItem)d;
            if ((bool)e.NewValue)
                //We set MenuItem attached property for current Icon
                HandleIconChanged(null, item, EventArgs.Empty);
            else
                //We clear the value of MenuItem attached property
                HandleIconChanged(item.Icon, item, EventArgs.Empty);
        }
    
        //By using an extension method we get hold of the old value without the need
        //to maintain any kind of dictionary, so we don't need to worry about memory leaks
        private static void HandleIconChanged(
            this object oldValue,
            object sender,
            EventArgs e)
        {
            var item = (MenuItem)sender;
            if (oldValue is DependencyObject oldIcon)
                SetMenuItem(oldIcon, null);
            if (item.Icon is DependencyObject newIcon)
                SetMenuItem(newIcon, item);
            //We need to remove the old handler, because it relates to the old icon
            DependencyPropertyDescriptor
                .FromProperty(MenuItem.IconProperty, item.GetType())
                .RemoveValueChanged(item, item.Icon.HandleIconChanged);
            //We add new handler, so that when the icon changes we get correct old icon
            DependencyPropertyDescriptor
                .FromProperty(MenuItem.IconProperty, item.GetType())
                .AddValueChanged(item, item.Icon.HandleIconChanged);
        }
    
        /**** The rest is just DP boilerplate code ****/
    
        private static readonly DependencyPropertyKey MenuItemPropertyKey =
            DependencyProperty.RegisterAttachedReadOnly(
                name: "MenuItem",
                propertyType: typeof(MenuItem),
                ownerType: typeof(MenuItemHelper),
                defaultMetadata: new PropertyMetadata(null));
    
        public static readonly DependencyProperty MenuItemProperty =
            MenuItemPropertyKey.DependencyProperty;
    
        public static MenuItem GetMenuItem(DependencyObject d)
            => (MenuItem)d.GetValue(MenuItemProperty);
    
        private static void SetMenuItem(DependencyObject d, MenuItem value)
            => d.SetValue(MenuItemPropertyKey, value);
    
        public static readonly DependencyProperty IsEnabledProperty =
            DependencyProperty.RegisterAttached(
                name: "IsEnabled",
                propertyType: typeof(bool),
                ownerType: typeof(MenuItemHelper),
                defaultMetadata: new PropertyMetadata(false, HandleIsEnabledChanged));
    
        public static bool GetIsEnabled(MenuItem item)
            => (bool)item.GetValue(IsEnabledProperty);
    
        public static void SetIsEnabled(MenuItem item, bool value)
            => item.SetValue(IsEnabledProperty, value);
    }
    

    然后您只需要在MenuItemHelper.IsEnabled="True"上设置MenuItem,然后您可以使用MenuItemHelper.MenuItem进行绑定(请记住它将在图标的根元素上设置 - {{ 1}}在你的情况下):

    Viewbox

    我个人最喜欢的是#III,因为它是三者中最多才多艺的,但也许在你的特殊情况下,其他解决方案可能更适用。

答案 1 :(得分:1)

因为它有点像XY problem,所以我会给你另一种方法来实现你的目标。

首先,您的appbar.folder.open资源过于复杂。 Canvas完全是多余的(您可以通过设置Path来抵消Margin)。这些数字在Path 19,24之内偏移,与Path中的Canvas相结合,导致必须将Viewbox与{{}一起使用1}}。此外,您的资源不可重用,因为它只能加载到可视树中一次,因此它只能在引用的最后一个位置可见(它将在以前的所有位置卸载)。我的建议是为ScaleTransform创建一个Style - 不仅可以重复使用,还可以扩展,因为您可以在应用后修改目标Path上的其他属性。样式。这是一个最小的风格来完成这项工作(我已经翻译了你的数据,以便它不再被抵消):

Path

其次,我不太明白你的<Style x:Key="appbar.folder.open" TargetType="{x:Type Path}"> <Setter Property="Fill" Value="Black" /> <Setter Property="Data" Value="M0,26 L9,10 L44,10 L35,26 Z M0,4 L16,4 C17,1 18.5,0 18.5,0 L29.75,0 C30.3,0 31,0.7 31,1.25 L31,4 L34,4 L34,8 L8,8 L0,22.4 Z" /> </Style> 附属财产的目的。在我的方法中,它是完全多余的。此外,我认为这是使设计人员在禁用项目代码时无法正确显示图标的部分。唯一的问题是,如果我们设置AttachedProperties.VisualIcon,我们会显示MenuItem.Icon="{DynamicResource appbar.folder.open}"文字而不是图标。 System.Windows.Style不支持将引用的资源转换为开箱即用的资源。但是,我们可以使用一个聪明的技巧来使事情有效 - 只需提供一个隐含的DynamicResourceExtensionDataTemplate,它将自动应用:

DataType="{x:Type Style}"

我们在<Style x:Key="MenuItemStyle" TargetType="{x:Type MenuItem}"> <Style.Resources> <DataTemplate DataType="{x:Type Style}"> <Path Style="{Binding}" Stretch="Uniform" HorizontalAlignment="Center" VerticalAlignment="Center" /> </DataTemplate> </Style.Resources> </Style> 上设置了一些其他属性,以便它非常适合图标区域。

现在我们需要做的就是引用样式和图标,让它显示在Path上:

MenuItem

这种方法的另一个优点是,即使禁用项目代码,它也可以在设计人员中工作(至少对我来说是这样)。

修改

如果要完全分离和自动化事物,可以继承<Menu (...)> <MenuItem Header="_Open" Style="{StaticResource MenuItemStyle}" Icon="{DynamicResource appbar.folder.open}" /> </Menu>

Style

并在资源字典中提供隐式public class PathStyle : Style { public PathStyle() { TargetType = typeof(Path); } }

DataTemplate

我将所有属性从<local:PathStyle x:Key="appbar.folder.open"> <Setter Property="Path.HorizontalAlignment" Value="Center" /> <Setter Property="Path.VerticalAlignment" Value="Center" /> <Setter Property="Path.Stretch" Value="Uniform" /> <Setter Property="Path.Fill" Value="Black" /> <Setter Property="Path.Data" Value="M0,26 L9,10 L44,10 L35,26 Z M0,4 L16,4 C17,1 18.5,0 18.5,0 L29.75,0 C30.3,0 31,0.7 31,1.25 L31,4 L34,4 L34,8 L8,8 L0,22.4 Z" /> </local:PathStyle> <DataTemplate DataType="{x:Type local:PathStyle}"> <Path Style="{Binding}" /> </DataTemplate> 移动到DataTemplate,以便可以在其他样式中覆盖它。请注意,您需要完全限定属性名称,即使用Style而不是简单Path.Data

现在您只需要在视图中引用资源:

Data

甚至:

<MenuItem Icon="{DynamicResource appbar.folder.open}" (...) />

所有的魔力都是由框架完成的。这种方法的优点在于您可以将<ContentPresenter Content="{DynamicResource appbar.folder.open}" /> 替换为包含例如:

ResourceDictionary
<Border x:Key="appbar.folder.open" x:Shared="False" Background="Red" />

一切仍然有效,无需修改视图。

答案 2 :(得分:0)

您的样式方法存在的问题是,在应用相同样式时,Viewbox将在多个项目之间共享。通过将Viewbox创建为具有x:Shared="False"的单独资源,可以避免这种情况。如果您想坚持当前的Canvas-Path方法,您应该将其转换为非共享资源,以便它可以重复使用

出于演示目的,我设置了MenuItem.Tag属性,但是附加属性的想法应该是一样的。

资源:

<!--Notice the added x:Shared-->
<Canvas x:Key="appbar.folder.open" x:Shared="False" Width="76" Height="76" Clip="F1 M 0,0L 76,0L 76,76L 0,76L 0,0">
    <Path Width="44" Height="26" Canvas.Left="19" Canvas.Top="24" Stretch="Fill" Fill="#FF000000" Data="F1 M 19,50L 28,34L 63,34L 54,50L 19,50 Z M 19,28.0001L 35,28C 36,25 37.4999,24.0001 37.4999,24.0001L 48.75,24C 49.3023,24 50,24.6977 50,25.25L 50,28L 53.9999,28.0001L 53.9999,32L 27,32L 19,46.4L 19,28.0001 Z "/>
</Canvas>

<!--Another icon for purpose of demonstration-->
<Canvas x:Key="appbar.folder.close" x:Shared="False" Width="76" Height="76" Clip="F1 M 0,0L 76,0L 76,76L 0,76L 0,0">
    <Path Width="44" Height="26" Canvas.Left="19" Canvas.Top="24" Stretch="Fill" Fill="#FFFF0000" Data="F1 M 19,50L 28,34L 63,34L 54,50L 19,50 Z M 19,28.0001L 35,28C 36,25 37.4999,24.0001 37.4999,24.0001L 48.75,24C 49.3023,24 50,24.6977 50,25.25L 50,28L 53.9999,28.0001L 53.9999,32L 27,32L 19,46.4L 19,28.0001 Z "/>
</Canvas>

<Viewbox x:Key="MenuItemStyle.Icon" x:Shared="False">
    <ContentControl Content="{Binding Path=Tag,RelativeSource={RelativeSource AncestorType=MenuItem}}" RenderTransformOrigin="0.5,0.5">
        <ContentControl.RenderTransform>
            <TransformGroup>
                <ScaleTransform ScaleX="2" ScaleY="2"/>
            </TransformGroup>
        </ContentControl.RenderTransform>
    </ContentControl>
</Viewbox>

<Style x:Key="MenuItemStyle" TargetType="MenuItem">
    <Setter Property="Icon" Value="{StaticResource MenuItemStyle.Icon}"/>
</Style>

用法:

<Menu x:Name="menu" Height="19" Margin="10,10,10.333,0" VerticalAlignment="Top" ItemContainerStyle="{StaticResource MenuItemStyle}">
    <MenuItem Header="_Open" Tag="{DynamicResource appbar.folder.open}"/>
    <MenuItem Header="_Open 2" Tag="{DynamicResource appbar.folder.open}"/>
    <MenuItem Header="_Close" Tag="{DynamicResource appbar.folder.close}"/>
</Menu>