So I created a custom button control, let's call it MyButton
, using Avalonia. MyButton
is a collection of several controls including a Avalonia.Controls.Button
looking like this (MyButton.xaml
):
<Border xmlns="https://github.com/avaloniaui"
.....
x:Class="myProject.myControls.MyButton">
<Button x:Name="button"
Background="Transparent"
....
BorderThickness="0">
<Panel >
.....
</Panel>
</Button>
</Border>
(Yes my custom control inherits from Avalonia.Controls.Border
instead of Avalonia.Controls.UserControl
)
My plan is to pass the buttons Command
property (the one with the x:Name="button"
attribute) further up and make it accessable via MyButton
.
So when I want to use MyButton
in the MainWindow.xaml
I'd to be able to do the following:
<Window ... >
<Design.DataContext>
<vm:MainWindowViewModel/>
</Design.DataContext>
<myControls:MyButton Command="{Binding MyButton_Click}"/>
</Window>
where the view model MainWindowViewModel.cs
looks like this:
public partial class MainWindowViewModel : ViewModelBase
{
public void MyButton_Click()
{
// do stuff...
}
}
The way I tried to do this in MyButton.xaml.cs
is the following:
public class MyButton : Border
{
private readonly Button button;
public MyButton()
{
InitializeComponent();
button = this.FindControl<Button>("button");
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
public static readonly StyledProperty<ICommand> CommandProperty =
AvaloniaProperty.Register<MyButton, ICommand>(nameof(Command));
public ICommand Command
{
get { return GetValue(CommandProperty); }
set
{ // this setter is never executed as can be seen when running with debugger attached
if (button != null)
{
button.Command = value;
SetValue(CommandProperty, value);
}
else
{
Debug.WriteLine("MyButton error: unable to set Command: control not initialized!");
}
}
}
}
However when running the application and clicking the button the target method MyButton_Click
is never executed. Attaching the debugger it seems like the MyButton.Command
setter is never executed either, which I think would be due to incorrect Binding? (There are no binding errors or anything related to this on the debug console)
After a few hours of trial and error I found a workaround using Reflection and a custom OnClick()
Eventhandler on the button
element. It works but is kinda ugly and requires a static target method so my question is:
How does one properly bind a Command on a UserControl to a method contained in the ViewModel of the main Window?
Also: Could my reflection-based approach also be viable? (I assume Avalonia bindings are also based on reflection somehow?)
Do not use getters and setters for styled properties, they won't be called when property is altered via bindings, styles or animations (it's the same for WPF, UWP and Xamarin.Forms). Instead you need to either bind your nested Button's command via <Button Command="{Binding $parent[myControls:MyButton]}" />
(preferable) or subscribe to property change notification from the static constructor like the original Button
does.
More on dependency properties (which work mostly the same way as StyledProperty in Avalonia): https://learn.microsoft.com/en-us/dotnet/framework/wpf/advanced/dependency-properties-overview