0

What is the best way to only enable context menu items when an image's Source property is not null? Here is the XAML:

    <Window.Resources>
        <local:IsNullValueConverter x:Key="IsNullValueConverter" />
        <local:NotNullValueConverter x:Key="NotNullValueConverter" />
    </Window.Resources>
    <DockPanel>
        <StackPanel DockPanel.Dock="Top" Orientation="Horizontal">
            <Button Content="Fill" IsEnabled="{Binding ElementName=TheImage, Path=Source, Converter={StaticResource IsNullValueConverter}}" Click="Fill_Click" />
            <Button Content="Erase" IsEnabled="{Binding ElementName=TheImage, Path=Source, Converter={StaticResource NotNullValueConverter}}" Click="Erase_Click" />
        </StackPanel>
        <Grid DockPanel.Dock="Bottom" Background="White">
            <Grid.ContextMenu>
                <ContextMenu>
                    <MenuItem Header="Fill" IsEnabled="{Binding ElementName=TheImage, Path=Source, Converter={StaticResource IsNullValueConverter}}" Click="Fill_Click" />
                    <MenuItem Header="Erase" IsEnabled="{Binding ElementName=TheImage, Path=Source, Converter={StaticResource NotNullValueConverter}}" Click="Erase_Click" />
                </ContextMenu>
            </Grid.ContextMenu>
            <Image Name="TheImage" />
        </Grid>
    </DockPanel>

and here is the code:

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void Fill_Click(object sender, RoutedEventArgs e)
        {
            int width = 1;
            int height = 1;
            var format = PixelFormats.Gray8;
            var pixels = new byte[width * height * (format.BitsPerPixel / 8)];
            TheImage.Source = BitmapSource.Create(width, height, 96.0, 96.0, format, null, pixels, width * (format.BitsPerPixel / 8));
        }

        private void Erase_Click(object sender, RoutedEventArgs e)
        {
            TheImage.Source = null;
        }
    }

    public class IsNullValueConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return value == null;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

    public class NotNullValueConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return value != null;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
}

The buttons react exactly how I want: only fill is enabled initially, then once you click it and the image is filled, it is disabled while erase becomes enabled. However the context menu items are both enabled the entire time, despite their IsEnabled properties using the exact same binding as the buttons.

Craig W
  • 4,390
  • 5
  • 33
  • 51
  • 1
    the issue is that the menu is not in the same binding scope as the buttons , menu is open at a popup which have a different datacontext, check out menu and binding issues [Added answer with an example] – ZSH Jan 26 '22 at 19:45

1 Answers1

0

I tried lots of different solutions and finally found something simple that worked:

<MenuItem Header="Fill" IsEnabled="{Binding Source={x:Reference TheImage}, Path=Source, Converter={StaticResource IsNullValueConverter}}" Click="Fill_Click" />
<MenuItem Header="Erase" IsEnabled="{Binding Source={x:Reference TheImage}, Path=Source, Converter={StaticResource NotNullValueConverter}}" Click="Erase_Click" />

Alternatively, one could add this to the code (have to name the context menu), but doing it in XAML seems so much more elegant.

NameScope.SetNameScope(TheContextMenu, NameScope.GetNameScope(this));

Thanks ZSH for pointing me in the right direction.

Craig W
  • 4,390
  • 5
  • 33
  • 51