在MenuItem.Icon中使用时,绑定失败

时间:2014-11-07 19:17:12

标签: wpf binding contextmenu

我想提供一个上下文菜单,其中的项目在空间中有一个颜色样本,其中"图标"通常放置这些菜单项,即与MenuItem.Icon对应的空间。

但是颜色样本是动态的 - UserControl上的一个Brush属性(在这个精心设计的例子中)变为随机颜色以响应ContextMenuOpening事件 - 我尝试绑定它失败。

运行时,菜单项在图标空间中没有内容,Visual Studio的输出包含一个看起来不应该发生的错误。

System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Controls.ContextMenu', AncestorLevel='1''. BindingExpression:Path=PlacementTarget.RandomBrush; DataItem=null; target element is 'Rectangle' (Name=''); target property is 'Fill' (type 'Brush')

这是控件的XAML:

<UserControl x:Class="ContextMenuItemIconTest.UserControl1"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         mc:Ignorable="d" 
         d:DesignHeight="300" d:DesignWidth="300">
<UserControl.ContextMenu>
    <ContextMenu>
        <MenuItem Header="Do something">
            <MenuItem.Icon>
                <Rectangle Width="16" Height="16" Fill="{Binding PlacementTarget.RandomBrush, RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}" />
            </MenuItem.Icon>
        </MenuItem>
    </ContextMenu>
</UserControl.ContextMenu>
<Grid>

</Grid>

背后的代码:

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace ContextMenuItemIconTest
{
    /// <summary>
    /// Interaction logic for UserControl1.xaml
    /// </summary>
    public partial class UserControl1 : UserControl
    {
        public UserControl1()
        {
            InitializeComponent();
            ContextMenuOpening += UserControl1_ContextMenuOpening;
        }

        void UserControl1_ContextMenuOpening(object sender, ContextMenuEventArgs e)
        {
            Random r = new Random();
            RandomBrush = new SolidColorBrush(Color.FromRgb((byte)r.Next(256), (byte)r.Next(256), (byte)r.Next(256)));
        }

        #region RandomBrush (Dependency Property)
        public Brush RandomBrush
        {
            get { return (Brush)GetValue(RandomBrushProperty); }
            set { SetValue(RandomBrushProperty, value); }
        }

        public static readonly DependencyProperty RandomBrushProperty =
            DependencyProperty.Register(
                "RandomBrush",
                typeof(Brush),
                typeof(UserControl1),
                new PropertyMetadata(new SolidColorBrush(Colors.Blue)));
        #endregion


    }
}

1 个答案:

答案 0 :(得分:0)

不确定是否有更好的解决方案,但我认为这里的方案非常棘手。 Icon内容似乎与视觉树完全分离。因此,您无法将绑定与RelativeSourceElementName一起使用,奇怪的是,即使将Source设置为某些{x:Reference}也会导致某些循环引用错误。

我只是想到这个解决方案,有点hacky但它​​是可以接受的。有关Freezable对象的有趣知识。其中的绑定(为某些属性设置)可以使用RelativeSource以及ElementName,即使它刚刚被声明为资源(添加到Resources)。因此,在这种情况下,我们可以尝试使用从Freezable派生的一些对象作为代理。由于此代理被声明为资源,因此我们可以将BindingIcon设置为Source,并将其设置为引用代理的某些StaticResource。然后它会工作。有许多来自Freezable的对象供您选择,您甚至可以创建自己的类。但是我想用这里存在的东西。 DiscreteObjectKeyFrame是最适合使用的对象。从技术上讲,它的Value属性可以包含任何类型的对象。现在是工作代码:

<ContextMenu>
    <ContextMenu.Resources>
       <!-- the proxy here -->
       <DiscreteObjectKeyFrame x:Key="o" KeyTime="0" 
                   Value="{Binding PlacementTarget.RandomBrush, 
                   RelativeSource={RelativeSource AncestorType=ContextMenu}}"/>
    </ContextMenu.Resources>

    <MenuItem Header="Do something">
        <MenuItem.Icon>
            <Rectangle Width="16" Height="16" 
                       Fill="{Binding Value, Source={StaticResource o}}" />
        </MenuItem.Icon>
    </MenuItem>
</ContextMenu>