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

如何使用itemcontainerStyle设置menuitem的图标

  •  5
  • dkackman  · 技术社区  · 14 年前

    下面是绑定a的示例 MenuItem 到数据对象。

    <Menu Grid.Row="0" KeyboardNavigation.TabNavigation="Cycle"
          ItemsSource="{Binding Path=MenuCommands}">  
        <Menu.ItemContainerStyle>
            <Style>
                <Setter Property="MenuItem.Header" Value="{Binding Path=DisplayName}"/>
                <Setter Property="MenuItem.ItemsSource" Value="{Binding Path=Commands}"/>
                <Setter Property="MenuItem.Command" Value="{Binding Path=Command}"/>
                <Setter Property="MenuItem.Icon" Value="{Binding Path=Icon}"/>
            </Style>
        </Menu.ItemContainerStyle>                
    </Menu>
    

    除了 菜单项 的图标显示为字符串 System.Drawing.Bitmap . 有问题的位图由数据对象从编译的资源返回。

    internal static System.Drawing.Bitmap folder_page
    {
        get
        {
            object obj = ResourceManager.GetObject("folder_page", resourceCulture);
            return ((System.Drawing.Bitmap)(obj));
        }
    }
    

    我做错什么了?

    5 回复  |  直到 7 年前
        1
  •  5
  •   Tim Cooper    13 年前

    WPF工作 ImageSource 不是,不是 System.Drawing 类。您需要绑定到 图像资源 . 您可以使用转换器来转换 Bitmap 图像资源 或者你可以放弃资源,以不同的方式做事。

        2
  •  9
  •   Community Egal    7 年前

    肯特(当然)有正确的答案。 但是我想我会继续发布转换器的代码,它从System.Drawing.Bitmap(Windows窗体)转换为System.Windows.Windows.Media.BitmapSource(WPF)。 …因为这是一个常见的问题。

    这需要三个步骤:

    1. 在绑定中使用图像转换器。
    2. 创建转换器。
    3. 在资源中声明转换器。

    下面是如何在绑定中使用图像转换器 :

    <Setter
        Property="MenuItem.Icon"
        Value="{Binding Path=Icon, Converter={StaticResource imageConverter}}"
    />
    

    这是转换器的代码 (将其放入名为imageconverter.cs的文件中)并将其添加到项目中:

    [ValueConversion(typeof(Image), typeof(string))]
    public class ImageConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            BitmapSource bitmapSource;
    
            IntPtr bitmap = ((Bitmap)value).GetHbitmap();
            try
            {
                bitmapSource = Imaging.CreateBitmapSourceFromHBitmap(bitmap, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
            }
            finally
            {
                DeleteObject(bitmap);
            }
    
            return bitmapSource;
        }
    
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return null;
        }
    
        [DllImport("gdi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
        static extern int DeleteObject(IntPtr o);
    }
    

    下面是您在资源部分声明它的方法 (请注意必须添加的本地命名空间):

    <Window
        x:Class="WpfApplication1.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfApplication2"
    >
        <Window.Resources>
            <local:ImageConverter x:Key="imageConverter"/>
        </Window.Resources>
        <!-- some xaml snipped for clarity -->
    </Window>
    

    就这样!


    更新

    在快速搜索类似问题后,我注意到 Lars Truijens 指出 here 以前的转换器实现泄漏。我已经更新了上面的转换器代码…这样它就不会泄漏。

    有关泄漏原因的更多信息,请参阅此msdn上的“备注”部分。 link .

        3
  •  1
  •   Isak Savo    10 年前

    WPF的菜单有点奇怪 不要 一起工作 ImageSource 对象类似于WPF框架的其余部分。

    最简单的方法是在ViewModel中简单地拥有一个返回完整的 Image 控制:

    public Image MenuIcon
    {
        get
        {
            return new Image()
                   {
                       Source = CreateImageSource("myImage.png")
                   };
        }
    }
    

    然后在你的 <Style> 对于菜单项(您可以在 ItemContainerStyle 例如)您只需绑定菜单项的 Icon 属性到 MenuIcon 视图模型中的属性:

    <Setter Property="Icon" Value="{Binding MenuIcon}" />
    

    有人可能会说,这打破了MVVM的精神,但在某一点上,你只是需要务实,然后转向更有趣的问题。

        4
  •  0
  •   Scott Whitlock    14 年前

    以下是我如何为菜单项创建视图模型: AbstractMenuItem . 特别注意图标区域:

        #region " Icon "
        /// <summary>
        /// Optional icon that can be displayed in the menu item.
        /// </summary>
        public object Icon
        {
            get
            {
                if (IconFull != null)
                {
                    System.Windows.Controls.Image img = new System.Windows.Controls.Image();
                    if (EnableCondition.Condition)
                    {
                        img.Source = IconFull;
                    }
                    else
                    {
                        img.Source = IconGray;
                    }
                    return img;
                }
                else
                {
                    return null;
                }
            }
        }
        private BitmapSource IconFull
        {
            get
            {
                return m_IconFull;
            }
            set
            {
                if (m_IconFull != value)
                {
                    m_IconFull = value;
                    if (m_IconFull != null)
                    {
                        IconGray = ConvertFullToGray(m_IconFull);
                    }
                    else
                    {
                        IconGray = null;
                    }
                    NotifyPropertyChanged(m_IconArgs);
                }
            }
        }
        private BitmapSource m_IconFull = null;
        static readonly PropertyChangedEventArgs m_IconArgs =
            NotifyPropertyChangedHelper.CreateArgs<AbstractMenuItem>(o => o.Icon);
    
        private BitmapSource IconGray { get; set; }
    
        private BitmapSource ConvertFullToGray(BitmapSource full)
        {
            FormatConvertedBitmap gray = new FormatConvertedBitmap();
    
            gray.BeginInit();
            gray.Source = full;
            gray.DestinationFormat = PixelFormats.Gray32Float;
            gray.EndInit();
    
            return gray;
        }
    
        /// <summary>
        /// This is a helper function so you can assign the Icon directly
        /// from a Bitmap, such as one from a resources file.
        /// </summary>
        /// <param name="value"></param>
        protected void SetIconFromBitmap(System.Drawing.Bitmap value)
        {
            BitmapSource b = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
                value.GetHbitmap(),
                IntPtr.Zero,
                Int32Rect.Empty,
                System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());
            IconFull = b;
        }
    
        #endregion
    

    您只需从这个类派生,并在构造函数中调用seticonfrombitmap并从resx文件传入一张图片。

    以下是我如何绑定到 Workbench Window :

        <Menu DockPanel.Dock="Top" ItemsSource="{Binding Path=(local:Workbench.MainMenu)}">
            <Menu.ItemContainerStyle>
                <Style>
                    <Setter Property="MenuItem.Header" Value="{Binding Path=(contracts:IMenuItem.Header)}"/>
                    <Setter Property="MenuItem.ItemsSource" Value="{Binding Path=(contracts:IMenuItem.Items)}"/>
                    <Setter Property="MenuItem.Icon" Value="{Binding Path=(contracts:IMenuItem.Icon)}"/>
                    <Setter Property="MenuItem.IsCheckable" Value="{Binding Path=(contracts:IMenuItem.IsCheckable)}"/>
                    <Setter Property="MenuItem.IsChecked" Value="{Binding Path=(contracts:IMenuItem.IsChecked)}"/>
                    <Setter Property="MenuItem.Command" Value="{Binding}"/>
                    <Setter Property="MenuItem.Visibility" Value="{Binding Path=(contracts:IControl.Visible), 
                        Converter={StaticResource BooleanToVisibilityConverter}}"/>
                    <Setter Property="MenuItem.ToolTip" Value="{Binding Path=(contracts:IControl.ToolTip)}"/>
                    <Style.Triggers>
                        <DataTrigger Binding="{Binding Path=(contracts:IMenuItem.IsSeparator)}" Value="true">
                            <Setter Property="MenuItem.Template">
                                <Setter.Value>
                                    <ControlTemplate TargetType="{x:Type MenuItem}">
                                        <Separator Style="{DynamicResource {x:Static MenuItem.SeparatorStyleKey}}"/>
                                    </ControlTemplate>
                                </Setter.Value>
                            </Setter>
                        </DataTrigger>
                    </Style.Triggers>
                </Style>
            </Menu.ItemContainerStyle>
        </Menu>
    
        5
  •  0
  •   Remco Schoeman    7 年前

    对于子孙后代:我想到了:

    <Menu.ItemContainerStyle>
        <Style TargetType="MenuItem">
            <Setter Property="Icon" Value="{Binding IconUrl, Converter={ns:UrlToImageConverter Width=16, Height=16}}"/>
        </Style>
    </Menu.ItemContainerStyle>
    

    转换器是 MarkupExtension 还有一个 IValueConverter ,因此您可以直接指定它,而不必使其成为静态资源。

    它使用 System.Windows.Media.ImageSourceConverter 将URI转换为 ImageSource 然后创建一个 Image 使用该源进行控制。 作为奖励,它使用 serviceProvider 提供给的参数 ProvideValue 所以它可以像wpf那样解析相对的图像URL。

    [ValueConversion(typeof(string), typeof(Image))]
    [ValueConversion(typeof(Uri), typeof(Image))]
    public class UrlToImageConverter : MarkupExtension, IValueConverter
    {
        public int? MaxWidth { get; set; }
    
        public int? MaxHeight { get; set; }
    
        public int? MinWidth { get; set; }
    
        public int? MinHeight { get; set; }
    
        public Stretch? Stretch { get; set; }
    
        public StretchDirection? StretchDirection { get; set; }
    
        private static readonly ImageSourceConverter _converter = new System.Windows.Media.ImageSourceConverter();
    
        private readonly IServiceProvider _serviceProvider;
    
        public UrlToImageConverter()
        {
            _serviceProvider = new ServiceContainer();
        }
    
        /// <summary>  </summary>
        private UrlToImageConverter(UrlToImageConverter provider, IServiceProvider serviceProvider)
        {
            _serviceProvider = serviceProvider ?? new ServiceContainer();
            MaxWidth = provider.MaxWidth;
            MaxHeight = provider.MaxHeight;
            MinWidth = provider.MinWidth;
            MinHeight = provider.MinHeight;
            Stretch = provider.Stretch;
            StretchDirection = provider.StretchDirection;
        }
    
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value == null) return null;
    
            var context = GetTypeDescriptorContext();
    
            bool canConvert;
            if (context == null)
                canConvert = _converter.CanConvertFrom(value.GetType());
            else
                canConvert = _converter.CanConvertFrom(context, value.GetType());
    
            if (canConvert)
            {
                if (context == null)
                    value = _converter.ConvertFrom(value);
                else
                    value = _converter.ConvertFrom(context, CultureInfo.CurrentCulture, value);
    
                if (value is ImageSource source)
                {
                    var img = new Image { Source = source };
                    if (MaxWidth != null) img.MaxWidth = MaxWidth.Value;
                    if (MaxHeight != null) img.MaxHeight = MaxHeight.Value;
                    if (MinWidth != null) img.MinWidth = MinWidth.Value;
                    if (MinHeight != null) img.MinHeight = MinHeight.Value;                    
                    img.Stretch = Stretch ?? System.Windows.Media.Stretch.Uniform;
                    img.StretchDirection = StretchDirection ?? System.Windows.Controls.StretchDirection.Both;
                    return img;
                }
            }
    
            return null;
        }
    
        private ITypeDescriptorContext GetTypeDescriptorContext()
        {
            if (_serviceProvider is ITypeDescriptorContext context)
                return context;
            else
                return (ITypeDescriptorContext)_serviceProvider?.GetService(typeof(ITypeDescriptorContext));
        }
    
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotSupportedException();
        }
    
        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            return new UrlToImageConverter(this, serviceProvider);
        }
    }