Search code examples
c#datagridcontextmenu

DataGridCell ContextMenu throws ArgumentNullException unless cell is pre-clicked


I'm trying to show a ContextMenu when right clicking a particular type of cell in a DataGridView. If I click the cell to select it before right-clicking to bring up the context menu it works as expected.

If I right-click the cell without clicking to select it first however, my application crashes because of an unhandled exception.

System.ArgumentNullException: 'Value cannot be null. (Parameter 'container')'

I've looked around and I think the answer from WPF Contextmenu itemtemplate commandParameter binding returns null is basically what I need to do but I don't understand how to apply this to my problem.

Edit: Here's the entire call stack

>   PresentationFramework.dll!System.Windows.Controls.ItemContainerGenerator.ItemFromContainer(System.Windows.DependencyObject container)   Unknown
    PresentationFramework.dll!System.Windows.Controls.ItemsControl.ItemInfoFromContainer(System.Windows.DependencyObject container) Unknown
    PresentationFramework.dll!System.Windows.Controls.DataGrid.HandleSelectionForCellInput(System.Windows.Controls.DataGridCell cell, bool startDragging, bool allowsExtendSelect, bool allowsMinimalSelect)    Unknown
    PresentationFramework.dll!System.Windows.Controls.DataGrid.OnContextMenuOpening(System.Windows.Controls.ContextMenuEventArgs e) Unknown
    PresentationFramework.dll!System.Windows.FrameworkElement.OnContextMenuOpeningThunk(object sender, System.Windows.Controls.ContextMenuEventArgs e)  Unknown
    PresentationFramework.dll!System.Windows.Controls.ContextMenuEventArgs.InvokeEventHandler(System.Delegate genericHandler, object genericTarget) Unknown
    PresentationCore.dll!System.Windows.RoutedEventArgs.InvokeHandler(System.Delegate handler, object target)   Unknown
    PresentationCore.dll!System.Windows.EventRoute.InvokeHandlersImpl(object source, System.Windows.RoutedEventArgs args, bool reRaised)    Unknown
    PresentationCore.dll!System.Windows.UIElement.RaiseEventImpl(System.Windows.DependencyObject sender, System.Windows.RoutedEventArgs args)   Unknown
    PresentationCore.dll!System.Windows.UIElement.RaiseTrustedEvent(System.Windows.RoutedEventArgs args)    Unknown
    PresentationCore.dll!System.Windows.UIElement.RaiseEvent(System.Windows.RoutedEventArgs args, bool trusted) Unknown
    PresentationFramework.dll!System.Windows.Controls.PopupControlService.RaiseContextMenuOpeningEvent(System.Windows.IInputElement source, double x, double y, bool userInitiated) Unknown
    PresentationFramework.dll!System.Windows.Controls.PopupControlService.ProcessMouseUp(object sender, System.Windows.Input.MouseButtonEventArgs e)    Unknown
    PresentationFramework.dll!System.Windows.Controls.PopupControlService.OnPostProcessInput(object sender, System.Windows.Input.ProcessInputEventArgs e)   Unknown
    PresentationCore.dll!System.Windows.Input.InputManager.RaiseProcessInputEventHandlers(System.Windows.Input.ProcessInputEventHandler postProcessInput, System.Windows.Input.ProcessInputEventArgs processInputEventArgs) Unknown
    PresentationCore.dll!System.Windows.Input.InputManager.ProcessStagingArea() Unknown
    PresentationCore.dll!System.Windows.Input.InputProviderSite.ReportInput(System.Windows.Input.InputReport inputReport)   Unknown
    PresentationCore.dll!System.Windows.Interop.HwndMouseInputProvider.ReportInput(System.IntPtr hwnd, System.Windows.Input.InputMode mode, int timestamp, System.Windows.Input.RawMouseActions actions, int x, int y, int wheel)   Unknown
    PresentationCore.dll!System.Windows.Interop.HwndMouseInputProvider.FilterMessage(System.IntPtr hwnd, MS.Internal.Interop.WindowMessage msg, System.IntPtr wParam, System.IntPtr lParam, ref bool handled)   Unknown
    PresentationCore.dll!System.Windows.Interop.HwndSource.InputFilterMessage(System.IntPtr hwnd, int msg, System.IntPtr wParam, System.IntPtr lParam, ref bool handled)    Unknown
    WindowsBase.dll!MS.Win32.HwndWrapper.WndProc(System.IntPtr hwnd, int msg, System.IntPtr wParam, System.IntPtr lParam, ref bool handled) Unknown
    WindowsBase.dll!MS.Win32.HwndSubclass.DispatcherCallbackOperation(object o) Unknown
    WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.InternalRealCall(System.Delegate callback, object args, int numArgs)  Unknown
    WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.TryCatchWhen(object source, System.Delegate callback, object args, int numArgs, System.Delegate catchHandler) Unknown
    WindowsBase.dll!System.Windows.Threading.Dispatcher.LegacyInvokeImpl(System.Windows.Threading.DispatcherPriority priority, System.TimeSpan timeout, System.Delegate method, object args, int numArgs)   Unknown
    WindowsBase.dll!MS.Win32.HwndSubclass.SubclassWndProc(System.IntPtr hwnd, int msg, System.IntPtr wParam, System.IntPtr lParam)  Unknown
    [Native to Managed Transition]  
    [Managed to Native Transition]  
    WindowsBase.dll!System.Windows.Threading.Dispatcher.PushFrameImpl(System.Windows.Threading.DispatcherFrame frame)   Unknown
    WindowsBase.dll!System.Windows.Threading.Dispatcher.PushFrame(System.Windows.Threading.DispatcherFrame frame)   Unknown
    WindowsBase.dll!System.Windows.Threading.Dispatcher.Run()   Unknown
    PresentationFramework.dll!System.Windows.Application.RunDispatcher(object ignore)   Unknown
    PresentationFramework.dll!System.Windows.Application.RunInternal(System.Windows.Window window)  Unknown
    PresentationFramework.dll!System.Windows.Application.Run()  Unknown
    Testproject.dll!Testproject.App.Main()  Unknown

Edit again; here is the full XAML

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>

        <Grid.Resources>
            <!-- Default DataTemplate -->
            <DataTemplate x:Key="DefaultDataTemplate">
                <TextBox Text="{Binding Path=Value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
            </DataTemplate>

            <!-- DataTemplate for Booleans -->
            <DataTemplate x:Key="BooleanDataTemplate">
                <CheckBox IsChecked="{Binding Path=Value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
            </DataTemplate>

            <!-- DataTemplate for Arrays -->
            <DataTemplate x:Key="ArrayDataTemplate">
                <DataGridCell Content="{Binding Path=Value, Mode=OneWay}" >
                    <DataGridCell.ContextMenu>
                        <ContextMenu>
                            <MenuItem Header="View / Edit..." />
                        </ContextMenu>
                    </DataGridCell.ContextMenu>
                </DataGridCell>
            </DataTemplate>

            <local:ValueDataTemplateSelector x:Key="templateSelector" 
                   DefaultDataTemplate="{StaticResource DefaultDataTemplate}" 
                   BooleanDataTemplate="{StaticResource BooleanDataTemplate}" 
                   ArrayDataTemplate="{StaticResource ArrayDataTemplate}" />

        </Grid.Resources>
        <DataGrid Name="DG2" ItemsSource="{Binding TagListView}" AutoGenerateColumns="False" Grid.Row="1">
            <DataGrid.Columns>
                <DataGridTextColumn Header="Name" Width="*" Binding="{Binding Path=Name, Mode=OneWay}" />
                <DataGridTemplateColumn Header="Value" Width="2*" CellTemplateSelector="{StaticResource templateSelector}" CellEditingTemplateSelector="{StaticResource templateSelector}" />
            </DataGrid.Columns>
        </DataGrid>
    </Grid>

Solution

  • I solved the problem.

    With reference to this XAML:

                <!-- DataTemplate for Arrays -->
                <DataTemplate x:Key="ArrayDataTemplate">
                    <DataGridCell Content="{Binding Path=Value, Mode=OneWay}" >
                        <DataGridCell.ContextMenu>
                            <ContextMenu>
                                <MenuItem Header="View / Edit..." />
                            </ContextMenu>
                        </DataGridCell.ContextMenu>
                    </DataGridCell>
                </DataTemplate>
    

    Any DataGridTemplateColumn a DataTemplate is applied to already contains DataGridCells in every row.

    So when I'm putting a DataGridCell inside the DataTemplate, I'm actually putting a DataGridCell inside the existing DataGridCell. This causes the problems when right clicking the cell. The ContextMenu is actually irrelevant, the problem occurs regardless.

    This can be fixed by using another type of Container in the DataTemplate, for example like this:

                <!-- DataTemplate for Arrays -->
                <DataTemplate x:Key="ArrayDataTemplate">
                    <TextBox Text="{Binding Path=Value, Mode=OneWay}" >
                        <TextBox.ContextMenu>
                            <ContextMenu>
                                <MenuItem Header="View / Edit..." Click="menuItem_Click" />
                            </ContextMenu>
                        </TextBox.ContextMenu>
                    </TextBox>
                </DataTemplate>