Crash when command parameter is MS.Internal.NamedObject "DisconnectedItem"
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
<Button Content="Click me!" Click="Button_Click" Width="100" Height="20">
<Button.ContextMenu>
<ContextMenu>
<MenuItem Header="Move To" ItemsSource="{Binding Destinations}">
<MenuItem.ItemContainerStyle>
<Style TargetType="MenuItem" BasedOn="{StaticResource {x:Type MenuItem}}">
<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}}"/>
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>
</ContextMenu>
</Button.ContextMenu>
</Button>
MainWindow.xaml.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
public partial class MainViewModel
{
public ObservableCollection<string> 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