WPF将基于几何的图像绑定到MenuItem.Icon属性

时间:2013-11-11 09:32:37

标签: c# .net wpf binding menu

我确信其他人找到了办法,我很接近但不完全。 我使用StaticResourceConverter类成功地使用基于资源名称绑定到资源的Geometry的Path对象:

public class StaticResourceConverter: BaseConverter, IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if(System.Convert.ToString(value).IsNullOrEmpty())
        {
            return null;
        }
        var resource = Application.Current.Resources[value];
        return resource;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new Exception("The method or operation is not implemented.");
    }
}

绑定到MenuItem中的Image对象需要使用相同的方法创建一个带有底层DrawingImage的Image实例:

<Style TargetType="{x:Type MenuItem}">
    <Setter Property="Icon">
        <Setter.Value>
            <Image Style="{StaticResource ResourceKey=SpMenuItemIcon}">
                <Image.Source>
                    <DrawingImage>
                        <DrawingImage.Drawing>
                            <GeometryDrawing
                                Brush="{StaticResource SpIconPath}"
                                Geometry="{Binding IconPath,
                                    Converter={converters:StaticResourceConverter}}"/>
                        </DrawingImage.Drawing>
                    </DrawingImage>
                </Image.Source>
            </Image>
        </Setter.Value>
    </Setter>
</Style>

问题是我怀疑只创建了一个Image实例,或者只绑定了1个实例,因为只有菜单中的最后一个图标才能显示正确的几何路径。为什么WPF只绑定最后一个MenuItem以及如何使它适用于所有菜单项?

更新

这里只是为了帮助测试,几个示例几何资源要绑定到(使用Key):

<Geometry x:Key="ExpandIconPath">F1 M 30.25,58L 18,58L 18,45.75L 22,41.75L 22,50.75L 30,42.75L 33.25,46L 25.25,54L 34.25,54L 30.25,58 Z M 58,45.75L 58,58L 45.75,58L 41.75,54L 50.75,54L 42.75,46L 46,42.75L 54,50.75L 54,41.75L 58,45.75 Z M 45.75,18L 58,18L 58,30.25L 54,34.25L 54,25.25L 46,33.25L 42.75,30L 50.75,22L 41.75,22L 45.75,18 Z M 18,30.25L 18,18L 30.25,18L 34.25,22L 25.25,22L 33.25,30L 30,33.25L 22,25.25L 22,34.25L 18,30.25 Z</Geometry>
<Geometry x:Key="CollapseIconPath">F1 M 54.2499,34L 42,34L 42,21.7501L 45.9999,17.7501L 45.9999,26.7501L 53.9999,18.7501L 57.2499,22.0001L 49.2499,30.0001L 58.2499,30.0001L 54.2499,34 Z M 34,21.7501L 34,34L 21.75,34L 17.75,30.0001L 26.75,30.0001L 18.75,22.0001L 22,18.7501L 30,26.7501L 30,17.7501L 34,21.7501 Z M 21.75,42L 34,42L 34,54.25L 30,58.25L 30,49.25L 22,57.25L 18.75,54L 26.75,46L 17.75,46L 21.75,42 Z M 42,54.25L 42,42L 54.2499,42L 58.2499,46L 49.2499,46.0001L 57.2499,54L 53.9999,57.25L 45.9999,49.25L 45.9999,58.25L 42,54.25 Z</Geometry>
<Geometry x:Key="CloseIconPath">F1 M 26.9166,22.1667L 37.9999,33.25L 49.0832,22.1668L 53.8332,26.9168L 42.7499,38L 53.8332,49.0834L 49.0833,53.8334L 37.9999,42.75L 26.9166,53.8334L 22.1666,49.0833L 33.25,38L 22.1667,26.9167L 26.9166,22.1667 Z</Geometry>

2 个答案:

答案 0 :(得分:5)

x:Shared属性设置为false,以防止在应用样式的所有位置重复使用样式资源,从而避免使用Image

<Style TargetType="{x:Type MenuItem}" x:Shared="False">
    <Setter Property="Icon">
        ...
    </Setter>
</Style>

答案 1 :(得分:5)

与Clemens建议的不同,您不需要使整个Style不共享。问题来自ImageMenuItem必须是唯一的Image。因此,只有将x:Shared设置为False时才将<Window x:Class="So19902960WpfMenuItemIconGeometry.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:So19902960WpfMenuItemIconGeometry" Title="MainWindow" Height="350" Width="525"> <Control.Resources> <local:StaticResourceConverter x:Key="StaticResourceConverter"/> <Geometry x:Key="ExpandIconPath">F1 M 30.25,58L 18,58L 18,45.75L 22,41.75L 22,50.75L 30,42.75L 33.25,46L 25.25,54L 34.25,54L 30.25,58 Z M 58,45.75L 58,58L 45.75,58L 41.75,54L 50.75,54L 42.75,46L 46,42.75L 54,50.75L 54,41.75L 58,45.75 Z M 45.75,18L 58,18L 58,30.25L 54,34.25L 54,25.25L 46,33.25L 42.75,30L 50.75,22L 41.75,22L 45.75,18 Z M 18,30.25L 18,18L 30.25,18L 34.25,22L 25.25,22L 33.25,30L 30,33.25L 22,25.25L 22,34.25L 18,30.25 Z</Geometry> <Geometry x:Key="CollapseIconPath">F1 M 54.2499,34L 42,34L 42,21.7501L 45.9999,17.7501L 45.9999,26.7501L 53.9999,18.7501L 57.2499,22.0001L 49.2499,30.0001L 58.2499,30.0001L 54.2499,34 Z M 34,21.7501L 34,34L 21.75,34L 17.75,30.0001L 26.75,30.0001L 18.75,22.0001L 22,18.7501L 30,26.7501L 30,17.7501L 34,21.7501 Z M 21.75,42L 34,42L 34,54.25L 30,58.25L 30,49.25L 22,57.25L 18.75,54L 26.75,46L 17.75,46L 21.75,42 Z M 42,54.25L 42,42L 54.2499,42L 58.2499,46L 49.2499,46.0001L 57.2499,54L 53.9999,57.25L 45.9999,49.25L 45.9999,58.25L 42,54.25 Z</Geometry> <Geometry x:Key="CloseIconPath">F1 M 26.9166,22.1667L 37.9999,33.25L 49.0832,22.1668L 53.8332,26.9168L 42.7499,38L 53.8332,49.0834L 49.0833,53.8334L 37.9999,42.75L 26.9166,53.8334L 22.1666,49.0833L 33.25,38L 22.1667,26.9167L 26.9166,22.1667 Z</Geometry> <Image x:Key="MenuItemImage" x:Shared="False" Width="16" Height="16"> <Image.Source> <DrawingImage> <DrawingImage.Drawing> <GeometryDrawing Brush="Blue" Geometry="{Binding Tag, RelativeSource={RelativeSource AncestorType=MenuItem}, Converter={StaticResource StaticResourceConverter}}"/> </DrawingImage.Drawing> </DrawingImage> </Image.Source> </Image> <Style TargetType="MenuItem"> <Setter Property="Icon" Value="{StaticResource MenuItemImage}"/> </Style> </Control.Resources> <Menu> <MenuItem Header="Expand" Tag="ExpandIconPath"/> <MenuItem Header="Collapse" Tag="CollapseIconPath"/> <MenuItem Header="Close" Tag="CloseIconPath"/> </Menu> </Window> 放入资源中,才能使using System; using System.Globalization; using System.Windows; using System.Windows.Data; namespace So19902960WpfMenuItemIconGeometry { public partial class MainWindow { public MainWindow () { InitializeComponent(); } } public class StaticResourceConverter : IValueConverter { public object Convert (object value, Type targetType, object parameter, CultureInfo culture) { if (value == DependencyProperty.UnsetValue || value == null) return DependencyProperty.UnsetValue; return Application.Current.MainWindow.Resources[value]; } public object ConvertBack (object value, Type targetType, object parameter, CultureInfo culture) { throw new NotSupportedException(); } } } 无法共享。

一个工作自包含的例子:

<强> MainWindow.xaml

NotSupportedException

<强> MainWindow.xaml.cs

Exception

注意:

  1. 您应该在ConvertBack方法中使用自定义消息抛出DependencyProperty.UnsetValue而不是Convert。它特别适用于未实施和不实施某种方法的情况。

  2. 您应该在{{1}}方法中检查{{1}}。当绑定失败时,转换器不应该使应用程序崩溃。