Search code examples
c#.net-coreavaloniaui

How to raise a custom event from child UserControl to parent control in AvaloniaUI


In dotnet's Avalonia-UI framework. I have a UserControl that is an "about" popup view with a "close" button, and should emit a custom event to the parent control to indicate that the popup should be closed by its parent window.

So far it seems I almost have it, but I can't find a way to raise the event. This is what I have:

In The AboutView.axaml I have:

<UserControl ... >
  <Button  Command="{Binding OnCloseView}" >Close</Button>

in the AboutViewModel.cs I have defined a ReactiveCommand:

public ReactiveCommand<Unit, Unit> OnCloseView { get; }

public AboutViewModel()
{
    OnCloseView = ReactiveCommand.Create(() => { });
}

in the code behind of the AboutView.axaml.cs I have:

public AboutView(AboutViewModel viewModel = null)
        {
            this.InitializeComponent();
            var dataContextViewModel = viewModel ?? new AboutViewModel();
            this.InitializeComponent();
            // ...
            dataContextViewModel.OnCloseView.Subscribe(x => {
                var eventArgs = new RoutedEventArgs();

// How do I raise the ExitView event from here?

                //this.RaiseEvent(eventArgs); // This didn't work, throws null reference exception.

                // This didn't work:
                //EventHandler handler = ExitView;
                //handler?.Invoke(this, eventArgs);

                });
            this.DataContext = dataContextViewModel;
        }

        public static readonly RoutedEvent<RoutedEventArgs> ExitViewEvent =
            RoutedEvent.Register<AboutView, RoutedEventArgs>(nameof(ExitView), RoutingStrategies.Bubble);

        public event EventHandler<RoutedEventArgs> ExitView
        {
            add => AddHandler(ExitViewEvent, value);
            remove => RemoveHandler(ExitViewEvent, value);
        }

In the parent MainWindow.axaml:

<views:AboutView ExitView="SomeCodeBehindEventHandler"  IsVisible="{Binding $parent.DataContext.IsAboutVisible}"/>

In the code-behind MainWindow.axaml.cs:

    public void OnAboutViewExitHandler(object sender, RoutedEventArgs e)
    {
        var viewModel = (MainWindowViewModel)this.DataContext;
        viewModel.OnCloseAboutPopup.Execute();
        e.Handled=true;
    }

The questions I have are:

  • How do I raise the RoutedEvent "ExitView" from code?

  • Is there a better way in axaml to bind the event to a ReactiveCommand in the parent's viewmodel? I couldn't find a way to bind it since this doesn't compile:

<views:AboutView ExitView="{Binding OnCloseAboutPopup}"/>

If I don't find a better way then I will have to pass a function to the code-behind and invoke the ReactiveCommand from there.

  • Is there any other better way to send the event from the child control to the parent?

Solution

  • You need to specify the routed event when constructing your event args.

    var eventArgs = new RoutedEventArgs { RoutedEvent = ExitViewEvent };