Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
1 of 4 tasks
DSPaul opened this issue Apr 20, 2024 · 0 comments
Open
1 of 4 tasks
Labels
bug 🐛 An unexpected issue that highlights incorrect behavior

Comments

@DSPaul
Copy link

DSPaul commented Apr 20, 2024

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
  • 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

@DSPaul DSPaul added the bug 🐛 An unexpected issue that highlights incorrect behavior label Apr 20, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug 🐛 An unexpected issue that highlights incorrect behavior
Projects
None yet
Development

No branches or pull requests

1 participant