CommunityToolkit / CommunityToolkit/dotnet

Crash when command parameter is MS.Internal.NamedObject "DisconnectedItem"

Open
#870 0 comments 0 reactions 0 assignees View on GitHub
bug :bug:
Dominant language
C#
Stars
3.8k
Forks
400
PR merge metrics
No merged PRs in 30d

Description

### Describe the bug

I have a contextmenu with one option "Move To" with some dynamically added sub-menuitems "destinations", which are create by binding the itemsource of the top menu item to an observableCollection of strings. All of these sub-menuitems share the same Command, and pass their header as the command parameter.

The first time I open the context menu and chose a destination, everything works fine. The second time you open the context menu however, the application crashes with the following error.

```
{"Parameter \"parameter\" (object) cannot be of type MS.Internal.NamedObject, as the command type requires an argument of type System.String. (Parameter 'parameter')"}
```

By putting a value converter on the command parameter binding and putting a breakpoint inside, I could inspect the problematic parameter and it is a `MS.Internal.NamedObject`, with name `DisconnectedItem`.

The only info I could find is this https://learn.microsoft.com/en-us/archive/msdn-technet-forums/36aec363-9e33-45bd-81f0-1325a735cc45 and this https://stackoverflow.com/questions/3868786/wpf-sentinel-objects-and-how-to-check-for-an-internal-type.

Stacktrace:
```
at CommunityToolkit.Mvvm.Input.RelayCommand`1.ThrowArgumentExceptionForInvalidCommandArgument(Object parameter)
at CommunityToolkit.Mvvm.Input.RelayCommand`1.CanExecute(Object parameter)
at System.Windows.Controls.MenuItem.UpdateCanExecute()
at System.Windows.Controls.MenuItem.OnCommandChanged(ICommand oldCommand, ICommand newCommand)
at System.Windows.FrameworkElement.OnPropertyChanged(DependencyPropertyChangedEventArgs e)
at System.Windows.DependencyObject.NotifyPropertyChange(DependencyPropertyChangedEventArgs args)
at System.Windows.DependencyObject.UpdateEffectiveValue(EntryIndex entryIndex, DependencyProperty dp, PropertyMetadata metadata, EffectiveValueEntry oldEntry, EffectiveValueEntry& newEntry, Boolean coerceWithDeferredReference, Boolean coerceWithCurrentValue, OperationType operationType)
at System.Windows.DependencyObject.InvalidateProperty(DependencyProperty dp, Boolean preserveCurrentValue)
at System.Windows.Data.BindingExpression.TransferValue(Object newValue, Boolean isASubPropertyChange)
at MS.Internal.Data.PropertyPathWorker.UpdateSourceValueState(Int32 k, ICollectionView collectionView, Object newValue, Boolean isASubPropertyChange)
at MS.Internal.Data.PropertyPathWorker.OnDependencyPropertyChanged(DependencyObject d, DependencyProperty dp, Boolean isASubPropertyChange)
at System.Windows.Data.BindingExpression.OnPropertyInvalidation(DependencyObject d, DependencyPropertyChangedEventArgs args)
at System.Windows.DependentList.InvalidateDependents(DependencyObject source, DependencyPropertyChangedEventArgs sourceArgs)
at System.Windows.DependencyObject.NotifyPropertyChange(DependencyPropertyChangedEventArgs args)
at System.Windows.DependencyObject.UpdateEffectiveValue(EntryIndex entryIndex, DependencyProperty dp, PropertyMetadata metadata, EffectiveValueEntry oldEntry, EffectiveValueEntry& newEntry, Boolean coerceWithDeferredReference, Boolean coerceWithCurrentValue, OperationType operationType)
at System.Windows.TreeWalkHelper.InvalidateTreeDependentProperty(TreeChangeInfo info, DependencyObject d, FrameworkObject& fo, DependencyProperty dp, FrameworkPropertyMetadata fMetadata, Style selfStyle, Style selfThemeStyle, ChildRecord& childRecord, Boolean isChildRecordValid, Boolean hasStyleChanged, Boolean isSelfInheritanceParent, Boolean wasSelfInheritanceParent)
at System.Windows.TreeWalkHelper.InvalidateTreeDependentProperties(TreeChangeInfo info, FrameworkElement fe, FrameworkContentElement fce, Style selfStyle, Style selfThemeStyle, ChildRecord& childRecord, Boolean isChildRecordValid, Boolean hasStyleChanged, Boolean isSelfInheritanceParent, Boolean wasSelfInheritanceParent)
at System.Windows.FrameworkElement.InvalidateTreeDependentProperties(TreeChangeInfo parentTreeState, Boolean isSelfInheritanceParent, Boolean wasSelfInheritanceParent)
at System.Windows.FrameworkElement.OnAncestorChangedInternal(TreeChangeInfo parentTreeState)
at System.Windows.TreeWalkHelper.OnAncestorChanged(DependencyObject d, TreeChangeInfo info, Boolean visitedViaVisualTree)
at MS.Internal.PrePostDescendentsWalker`1._VisitNode(DependencyObject d, Boolean visitedViaVisualTree)
at System.Windows.DescendentsWalker`1.WalkLogicalChildren(FrameworkElement feParent, FrameworkContentElement fceParent, IEnumerator logicalChildren)
at System.Windows.DescendentsWalker`1.WalkFrameworkElementLogicalThenVisualChildren(FrameworkElement feParent, Boolean hasLogicalChildren)
at System.Windows.DescendentsWalker`1.IterateChildren(DependencyObject d)
at MS.Internal.PrePostDescendentsWalker`1._VisitNode(DependencyObject d, Boolean visitedViaVisualTree)
at System.Windows.DescendentsWalker`1.WalkLogicalChildren(FrameworkElement feParent, FrameworkContentElement fceParent, IEnumerator logicalChildren)
at System.Windows.DescendentsWalker`1.WalkFrameworkElementLogicalThenVisualChildren(FrameworkElement feParent, Boolean hasLogicalChildren)
at System.Windows.DescendentsWalker`1.IterateChildren(DependencyObject d)
at System.Windows.DescendentsWalker`1.StartWalk(DependencyObject startNode, Boolean skipStartNode)
at MS.Internal.PrePostDescendentsWalker`1.StartWalk(DependencyObject startNode, Boolean skipStartNode)
at System.Windows.TreeWalkHelper.InvalidateOnTreeChange(FrameworkElement fe, FrameworkContentElement fce, DependencyObject parent, Boolean isAddOperation)
at System.Windows.Controls.Primitives.Popup.CreateWindow(Boolean asyncCall)
at System.Windows.Controls.Primitives.Popup.OnIsOpenChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
at System.Windows.FrameworkElement.OnPropertyChanged(DependencyPropertyChangedEventArgs e)
at System.Windows.DependencyObject.NotifyPropertyChange(DependencyPropertyChangedEventArgs args)
at System.Windows.DependencyObject.UpdateEffectiveValue(EntryIndex entryIndex, DependencyProperty dp, PropertyMetadata metadata, EffectiveValueEntry oldEntry, EffectiveValueEntry& newEntry, Boolean coerceWithDeferredReference, Boolean coerceWithCurrentValue, OperationType operationType)
at System.Windows.DependencyObject.InvalidateProperty(DependencyProperty dp, Boolean preserveCurrentValue)
at System.Windows.Data.BindingExpression.TransferValue(Object newValue, Boolean isASubPropertyChange)
at MS.Internal.Data.PropertyPathWorker.UpdateSourceValueState(Int32 k, ICollectionView collectionView, Object newValue, Boolean isASubPropertyChange)
at MS.Internal.Data.PropertyPathWorker.OnDependencyPropertyChanged(DependencyObject d, DependencyProperty dp, Boolean isASubPropertyChange)
at System.Windows.Data.BindingExpression.OnPropertyInvalidation(DependencyObject d, DependencyPropertyChangedEventArgs args)
at System.Windows.DependentList.InvalidateDependents(DependencyObject source, DependencyPropertyChangedEventArgs sourceArgs)
at System.Windows.DependencyObject.NotifyPropertyChange(DependencyPropertyChangedEventArgs args)
at System.Windows.DependencyObject.UpdateEffectiveValue(EntryIndex entryIndex, DependencyProperty dp, PropertyMetadata metadata, EffectiveValueEntry oldEntry, EffectiveValueEntry& newEntry, Boolean coerceWithDeferredReference, Boolean coerceWithCurrentValue, OperationType operationType)
at System.Windows.DependencyObject.SetValueCommon(DependencyProperty dp, Object value, PropertyMetadata metadata, Boolean coerceWithDeferredReference, Boolean coerceWithCurrentValue, OperationType operationType, Boolean isInternal)
at System.Windows.DependencyObject.SetValue(DependencyProperty dp, Object value)
at CrashOnDisconnectedItem.MainWindow.Button_Click(Object sender, RoutedEventArgs e) in C:\Users\pauld\Documents\dev\CrashOnDisconnectedItem\CrashOnDisconnectedItem\MainWindow.xaml.cs:line 23
```

### Regression

_No response_

### Steps to reproduce

I made a minimal repo to reproduce the crash https://github.com/DSPaul/CrashOnDisconnectedItem.

Simply click the button and chose a destination option, you will see a messagebox showing that it works. Click the btn again, you will see another messagebox showing the exception because it crashed.

The repo consists of these 3 simple files:
MainWindow.xaml
``` xaml






<Setter Property="MenuItem.Command" Value="{Binding Path=DataContext.MoveCommand,
RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type MenuItem}}}"/>
<Setter Property="MenuItem.CommandParameter" Value="{Binding Header, RelativeSource={RelativeSource self}}"/>




```

MainWindow.xaml.cs
``` cs
public partial class MainWindow : Window
{
public MainWindow()
{
DataContext = new MainViewModel();
InitializeComponent();
}

private void Button_Click(object sender, RoutedEventArgs e)
{
try
{

Button btn = (Button)sender;
btn.ContextMenu!.PlacementTarget = btn;
btn.ContextMenu!.IsOpen = !btn.ContextMenu!.IsOpen;
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString(), "crash", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
}
```
MainViewModel
``` cs
public partial class MainViewModel
{
public ObservableCollection Destinations { get; } = ["loc1", "loc2", "loc3"];

[RelayCommand]
public void Move(string? destination)
{
if (string.IsNullOrEmpty(destination))
{
throw new ArgumentNullException(nameof(destination));
}

//do something here
MessageBox.Show($"Moved something to {destination}");
}
}
```

### Expected behavior

Definitely not crash, when CanExcecute is called with DisconnectedItem as parameter, it should probably just return false.

### Screenshots

_No response_

### IDE and version

VS 2022

### IDE version

Version 17.9.1

### Nuget packages

- [ ] CommunityToolkit.Common
- [ ] CommunityToolkit.Diagnostics
- [ ] CommunityToolkit.HighPerformance
- [X] CommunityToolkit.Mvvm (aka MVVM Toolkit)

### Nuget package version(s)

8.2.2

### Additional context

_No response_

### Help us help you

No, just wanted to report this

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.