5

I am using ReactiveUI and Avalonia with C#.

I have problem with executing command on a Button inside ItemTemplate of MyItemsControl. MyView and MyItemsControl are correctly displayed but when I click a Button generated inside ItemsTemplate, nothing happens, even though binding to command does not report any errors. Here is a mock-up of my code.

View MyView:

<UserControl
    ...
    x:Class="MyApp.MyView"
    xmlns:vm="using:MyApp.ViewModels"
    xmlns:mycontrols="clr-namespace:MyApp.MyContorls" 
    DataContext="vm:MyViewModel">
    <!--Other code-->
    <mycontrols:MyItemsControl
        Name=“MyItemsControl1“>
    <!--Other code-->
</UserControl>

MyView code behind:

public partial class MyView : IViewFor<MyViewModel>
{
    public MyItemsControl MyItemsControl1 => this.FindControl<MyItemsControl>("MyItemsControl1");

    public MyView()
    {
        this.WhenActivated(disposables =>
        {
            this.BindCommand(ViewModel, vm => vm.RemoveItemCommand, v => v.MyItemsControl1.RemoveThisItemCommand)
                .DisposeWith(disposables);
        }
        AvaloniaXamlLoader.Load(this);
    }
}

ViewModel MyViewModel:

public class MyViewModel : ReactiveObject
{
    //other code
    private ReactiveCommand<SomeItem, Unit>? _removeItemCommand;
    public ReactiveCommand<SomeItem, Unit>? RemoveItemCommand
    {
        get { return _removeItemCommand; }
        private set { this.RaiseAndSetIfChanged(ref _removeItemCommand, value); }
    }
    //other code
}

Here is custom UserControl MyItemsControl:

<UserControl
    x:Class="MyApp.MyControls.MyItemsControl"
    Name=“ParentUserControl“>
    <!--Other code-->
    <ItemsControl
        Items={Binding #ParentUserControl.SomeItems}>
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <Button
                    Command={Binding #ParentUserControl.RemoveThisItemCommand}
                    CommandParameter="{Binding .}"/>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
</UserControl>

MyUserControl code behind:

public partial class MyUserControl : UserControl
{
    public static readonly DirectProperty<MyItemsControl, ReactiveCommand<SomeItem, Unit>> RemoveThisItemCommandProperty =
        AvaloniaProperty.RegisterDirect<MyItemsControl, ReactiveCommand<SomeItem, Unit>>(nameof(RemoveThisItemCommand), x => x. RemoveThisItemCommand, (x, v) => x.RemoveThisItemCommand = v);

    private ReactiveCommand<SomeItem, Unit> _removeThisItemCommand;
    public ReactiveCommand<SomeItem, Unit> RemoveThisItemCommand
    {
        get => _removeThisItemCommand;
        set => SetAndRaise(RemoveThisItemCommandProperty, ref _removeThisItemCommand, value);
    }
}

Code compiles fine, and displays MyView and MyUserControl as expected, but nothing happens when I click a button which is generated by ItemTemplate.

I also tried declaring RemoveThisItemCommandProperty as a StyledProperty, and tried using ICommand instead of ReactiveCommand in MyUserControl code behind. But nothing works.

If I use ICommand instead of ReactiveCommand, and bind from xaml in MyView instead of constructor it works as expected and parameter is passed through to command:

<mycontrols:MyItemsControl
        Name="MyItemsControl1"
        RemoveThisItemCommand="{Binding RemoveItemCommand}">

But I wanted to bind in constructor, since it is recommended to do so by documentation here. Is there a way to bind this button command with parameter in view constructor?

ASh
  • 34,632
  • 9
  • 60
  • 82
Miljac
  • 2,045
  • 4
  • 19
  • 28
  • The examples in the docs call `AvaloniaXamlLoader.Load(this);` in the view behind, did you leave that out? – Funk Mar 03 '23 at 08:00
  • I left it out in this example by accident, will add it now, it is present in my code – Miljac Mar 03 '23 at 08:01
  • 1
    Have you tried using `StyledProperty` instead of `DirectProperty`? The way you set the binding and/or the fact that you bind to the parent in the item template may somehow make it break. `In general you should declare your properties as styled properties`: https://docs.avaloniaui.net/docs/authoring-controls/defining-properties#when-to-use-a-direct-vs-a-styled-property – radoslawik Mar 03 '23 at 12:23
  • I have tried it before, yes, but tried it again now just to make sure, it doesn't help – Miljac Mar 06 '23 at 07:14

1 Answers1

-1

Reduce your binding complexity.

In your SomeItem object, create a command property and bind that to the button command.

<ItemsControl Items={Binding #ParentUserControl.SomeItems}>
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Button
                Command={Binding RemoveThisItemCommand}
                CommandParameter="{Binding .}"/>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

When creating your list of SomeItem instances, inject your main view model command into that property.

Jammer
  • 9,969
  • 11
  • 68
  • 115
  • While it would work, it isn't what I'm looking for, command to remove item from the list has no business being on the item itself. – Miljac Mar 09 '23 at 10:01
  • Indeed. Because you're looking for the wrong thing. Through all my years of WPF and AvaloniaUI, when I encounter an issue like this, the solution is *always* to reduce your binding complexity. Or, create and fire a custom event ... – Jammer Mar 09 '23 at 19:59