Search code examples
c#xamlavaloniauiavalonia

How to bind Command to parent's ViewModel from ContextMenu in Avalonia?


I have ListBox with ItemsSource binded to ObservableCollection in ViewModel and I need to show ContextMenu for each item in ListBox. ListBox looks like this:

<ListBox x:Name="DriversPanel" ItemsSource="{Binding Employees}">
     <ListBox.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="{Binding Name}"/>
                <StackPanel.ContextMenu>
                    <ContextMenu>
                        <MenuItem Command="{BINDING TO FUNCTION "ButtonAction" HERE}" Header="Some action"/>
                    </ContextMenu>
                </StackPanel.ContextMenu>
            </StackPanel>
        </DataTemplate>
    </ListBox.ItemTemplate>
    <ListBox.Styles>
        <Style Selector="ListBoxItem">
            <!--style settings-->
        </Style>
    </ListBox.Styles>           
</ListBox>

here I am facing an error, when I write only Command="{Binding ButtonAction}" it looks for this function in Employee object. But this function is in the main ViewModel. Viwmodel is working because binding to Employees ObservableCollection works well. I tried these solutions:

<MenuItem Command="{Binding DataContext.ButtonAction, RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=Window}}" Header="Some action"/>

and

<MenuItem Command="{Binding $parent[ListBox].DataContext.ButtonAction}" Header="Some action"/>

but none of them worked. The function is created in ViewModel which looks like this:

internal class MainViewModel : INotifyPropertyChanged
{
   public event PropertyChangedEventHandler? PropertyChanged;

   protected virtual void OnPropertyChanged(string propertyName)
   {
      PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
   }

   private ObservableCollection<Employee> _employes = new ObservableCollection<Employee>();
   public ObservableCollection<Employee> Employees
   {
       get => _employes;
       set
       {
          _employes = value;
          OnPropertyChanged(nameof(Employees));
       }
   }

   public void ButtonAction()
   {
       Console.WriteLine("It works!");
       OnPropertyChanged(nameof(Employees));
   }
}

Employee is just an ordinary object:

internal class Employee
{
   public int ID { get; set; }
   public string Name { get; set; }
   public int Age { get; set; }  
}

Any ideas please?


Solution

  • So I found a solution. For binding to parent's ViewModel I need to use this:

    <MenuItem Command="{Binding #DriversPanel((vm:MainViewModel)DataContext).ButtonAction}" Header="Some action"/>
    

    Reference on GitHub here