avaloniauiavalonia

Creating a dynamic amount of buttons based on a ObservableCollection using AvaloniaUI


I'm trying to create a ScrollViewer and/or ComboBox that contains a dynamic amount of buttons based on an Observable Collection. My current approach creates a dynamic amount of buttons with the correct labels but none of them are enabled, my guess is that the Command Binding is getting messed up, but I'm not sure how to fix that.

This is the ViewModel

namespace ProjectAtwood.Presentation.ViewModels
{
    public partial class DynamicUserOverviewViewModel : ObservableRecipient
    {
        #region fields
        private readonly IUnitOfWork _unitOfWork;
        ViewSelect view;
        [ObservableProperty]
        private ObservableCollection<User> _users; //= new ObservableCollection<User>();
        #endregion

        private void FillWithContent()
        {
            Users  = new ObservableCollection<User>(_unitOfWork.RepositoryUser.SelectAll());
        }
        protected override void OnActivated()
        {
            this.FillWithContent();
            base.OnActivated();
        }
        [RelayCommand]
        private void NavUserProfile(Guid userId)
        {
            WeakReferenceMessenger.Default.Send(new ProfileUserChangeMessage(userId));
            view = ViewSelect.UserProfileAdminView;
            WeakReferenceMessenger.Default.Send(new CurrentPageChangeMessage(view));
        }

And this is the axaml Code of the view implementing the ScrollViewer containing the buttons

<ScrollViewer HorizontalAlignment="Center"
                  Background="{StaticResource DarkGrey}"
                  VerticalScrollBarVisibility="Visible"
                  MaxHeight="250">
            <!--  ItemControl to display the observable collection Buttons  -->
            <ItemsControl ItemsSource="{Binding Users}">
                <!--  The controls to display each item, bind them to properties  -->
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                            <Button Content="{Binding Username}"
                                    CommandParameter="{Binding UserId}"
                                    Command="{Binding NavUserProfileCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type vm:DynamicUserOverviewViewModel}}}"
                                    />
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
                <!--  Display the data from the Datasource  -->
                <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                            <StackPanel/>
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>
            </ItemsControl>
    </ScrollViewer>

Solution

  • Refer to docs on How to bind to an Ancestor

    To access a property of a parent's DataContext it will be necessary to cast it with a casting expression (vm:MyUserControlViewModel)DataContext to its actual type. Otherwise DataContext would be considered as of type object and accessing a custom property would result in an compile-time error.

    You need to cast your parent DataContext to DynamicUserOverviewViewModel

    1. add the namespace for your view models in your axaml
    xmlns:vm ="using:ProjectAtwood.Presentation.ViewModels"
    
    1. then use the following syntax
    Command="{Binding $parent[ItemsControl].((vm:DynamicUserOverviewViewModel)DataContext).NavUserProfileCommand}"
    

    it's simply saying search of parent of type ItemsControl and cast it's DataContext as DynamicUserOverviewViewModel then bind to NavUserProfileCommand