Avalonia icon indicating copy to clipboard operation
Avalonia copied to clipboard

Allow usage of bindings/commands for event handlers.

Open grokys opened this issue 4 years ago • 28 comments

Avalonia, like WPF and UWP has two different ways of handling interactions: events and commands. This can be confusing for new users, and awkward for experienced users because:

  • New users don't understand the difference and try to use bindings in event handlers
  • Experienced users often want to pass an event to the view model and can't do that without adding codebehind or using a behavior

What if it were possible to put bindings to commands and methods in event handlers? There are two questions I think:

  • What would the handler command/method look like?
  • How would it be implemented?

Some ideas:

Methods

<Border PointerPressed="{Binding MyMethod}">

The possible signatures for this method could be:

void MyMethod(); // No parameters
void MyMethod(PointerPressedEventArgs e); // Event args
void MyMethod(object sender, PointerPressedEventArgs e); // Sender and event args

Would we want to accept all of these or just some?

Commands

<Border PointerPressed="{Binding MyCommand}">

The event args could probably be passed as the CommandParameter?

Thoughts?

Any thoughts on this? Good idea? Bad idea? Would it make things more or less confusing?

grokys avatar Apr 09 '20 10:04 grokys

I think we should accept all.

lindexi avatar Apr 09 '20 11:04 lindexi

The most confusing for new users (like I am/was) is that events are not visible in Intellisense. I have no idea which events exist without knowing the source code in detail. This should be fixed first in my opinion, otherwise this enhancement will not do much for new user experience.

After this is fixed, I agree with your idea. All should be accepted. I wouldn't want to use CommandParameter however since it would prevent using this by the user. I mean what happens if the user tries something like this: <Button PointerPressed="{Binding MyMethod}" CommandParameter={....}>? Which gets overridden? Which gets ignored? Does this even produce an error in XAML or on publish?

Symbai avatar Apr 09 '20 11:04 Symbai

I have mixed feelings, I've sometimes wanted to sent a click command without using a button. I don't think I would use it as described as I don't generally want to send view specific objects to the viewmodel. I guess you could use converters to convert from PointerPressedEventArgs to something view agnostic but that is getting complex again. I can see in non mvvm cases where the datacontext could be the view that this might be useful and some may be quite happy with view objects in their view models. I wonder if the CommandParameter could be bound to properties of the event.

ahopper avatar Apr 09 '20 11:04 ahopper

CommandParameter should always belong to the bound Command. If you bind a Command to an event the event args should be used as parameter. My opinion.

Gillibald avatar Apr 09 '20 11:04 Gillibald

I wouldn't want to use CommandParameter however since it would prevent using this by the user. I mean what happens if the user tries something like this: <Button PointerPressed="{Binding MyMethod}" CommandParameter={....}>?

CommandParameter for sending data to event handlers doesn't make much sense: there's only a single CommanParameter property, and only on certain classes.

How would CommandParameter work with multiple event handlers?

<Button PointerPressed="{Binding MyMethod}" DragStart="{Binding AnotherMethod}" CommandParameter={....}>

IMO CommandParameter can only affect the parameters sent to the Command property as @Gillibald says.

grokys avatar Apr 09 '20 11:04 grokys

If you wanted CommandParameter to work I guess you would have one for each event PointerPressedParameter

ahopper avatar Apr 09 '20 11:04 ahopper

It is totally fine to process event args in the ViewModel. An event isn't tied to the view. You can even raise these events in a unit test.

Gillibald avatar Apr 09 '20 11:04 Gillibald

It makes the ViewModel less platform agnostic, that may not matter a lot of the time.

ahopper avatar Apr 09 '20 11:04 ahopper

To be honest I'd rather have some way to embed inline C# code. Event args are way too platform specific to be passed directly to the view model, so there should be some kind of a medium that would convert those to platform-independent call.

kekekeks avatar Apr 09 '20 12:04 kekekeks

To not overcomplicate things we can just ignore the event args. This is all about making things simpler. Inline code is a different feature that can be useful.

Gillibald avatar Apr 09 '20 12:04 Gillibald

To my current understanding, there is currently no way to access events like PointerEnter without using code-behind or libraries like https://github.com/wieslawsoltes/AvaloniaBehaviors, right? So this would be, from my user perspective, a great addition.

MarcusWichelmann avatar Apr 10 '20 22:04 MarcusWichelmann

In UWP we have x:Bind feature: Event Binding Event binding is a unique feature for compiled binding. It enables you to specify the handler for an event using a binding, rather than it having to be a method on the code behind. For example: Click="{x:Bind rootFrame.GoForward}".

For events, the target method must not be overloaded and must also:

  • Match the signature of the event.
  • OR have no parameters.
  • OR have the same number of parameters of types that are assignable from the types of the event parameters.

maxkatz6 avatar Apr 11 '20 01:04 maxkatz6

In my opinion it's useful feature in UWP, but isn't friendly for MVVM. Maybe it should allow to pass custom parameters: Click="{Binding ViewModel.RunCommand(ViewModel.Parameter)}" or Click="{Binding ViewModel.SearchCommand(SomeTextBox.Text)}" or Click="{Binding ViewModel.CalculateMethod(SomeTextBox.Text.Length, 5)}"

However, it should depend on DataContext with classic Binding, as it more expected for non-UWP developers.

maxkatz6 avatar Apr 11 '20 01:04 maxkatz6

DevExpress MVVM has several really nice extensions to enable MVVM shortcuts. Maybe take a look at these for some ideas?

https://docs.devexpress.com/WPF/115770/mvvm-framework/dxbinding

aetaylor avatar Jan 29 '21 16:01 aetaylor

@aetaylor it should be possible to port this markup extension in Avalonia too, if there is any source code. But I guess it heavily relies on reflection.

maxkatz6 avatar Jan 30 '21 10:01 maxkatz6

I think this is a great idea, it needs to be added

RomanSoloweow avatar Jul 29 '21 05:07 RomanSoloweow

seems this has been done here, url.

ymg2006 avatar Aug 21 '22 05:08 ymg2006

Coming from Angular, I really though this would have been already in Avalonia. How would one go about natively implementing a pointerpressed event from XAML on a control without making a custom control? (In MVVM)

Edit: Also if I were to pick one, I'd do:

void MyMethod(object sender, PointerPressedEventArgs e); // Sender and event args

cyraid avatar May 20 '23 20:05 cyraid

To be honest I'd rather have some way to embed inline C# code. Event args are way too platform specific to be passed directly to the view model, so there should be some kind of a medium that would convert those to platform-independent call.

Not sure I understand, though I'm very new to Avalonia. Wouldn't it be the same as doing:

public void OnPointerPressed(object sender, PointerPressedEventArgs args);

Or am I missing something deeper? Because if the compiler does output that, then it'd be the same as me writing an override anyway.

cyraid avatar May 20 '23 22:05 cyraid

any progress on this?

also a an alternative i think you could use ReactiveWindow & this.WhenActivated(action => ...) to add/remove event handlers (sure xaml only version would be nice, but this is doable now ...)

CzBuCHi avatar Sep 05 '23 07:09 CzBuCHi

Still Avalonia.Behaviors is the way to go if you need that functionality. Esp. custom events can be very powerful.

timunie avatar Sep 05 '23 07:09 timunie

Still Avalonia.Behaviors is the way to go if you need that functionality. Esp. custom events can be very powerful.

@timunie correct me if I'm wrong but, doesn't Avalonia.Behaviors take a lot of boiler plate for each event? When I looked at it (which wasn't too long) it's definitely not as simple as setting an attribute in xaml.

cyraid avatar Sep 05 '23 08:09 cyraid

Yeah, having a way to bind to it would be a great feature, but it's not that high on our list atm. If someone wants to contribute it, I guess it is welcome. But the API should be discussed before one starts.

timunie avatar Sep 05 '23 08:09 timunie

@timunie Maybe just copy code from https://github.com/Serg046/EventBinder?

  1. Licensed under MIT (so compatible with Avalonia?)
  2. Supports constants (numbers, strings) and bindings as method parameters (so people who worry about cross-platform MVVM should be satisfied?)
  3. Path to method supports only . (can potentially be extended with the full syntax, but fine as is?)
  4. Supports $0, $1 for passing event arguments (doesn't seem to conflict with Avalonia's syntax?)
  5. Relies on IL (lack of compiled bindings can be an issue in some scenarios, but this can get implemented later?)

Also code completion would need to include events ideally. Getting JetBrains to update ReSharper (it complains about bindings to events and doesn't include them in completion but does highlight them) is a different story but can probably happen within 2-3 years.

Athari avatar Nov 09 '23 15:11 Athari

Interesting project and idea. Maybe this should be added to https://github.com/AvaloniaCommunity/awesome-avalonia . If you have some free time, please provide a PR to them.

timunie avatar Nov 10 '23 08:11 timunie

@Athari no, we can't copy paste anything. Because it should be integrated into XamlX compiler and not be a markup extensions, and EventBinding syntax wasn't even discussed here before. The most promising was mix of @grokys proposal and/or x:Bind.

But either way, EventBinding port to Avalonia is a great addition to the ecosystem!

maxkatz6 avatar Nov 12 '23 01:11 maxkatz6

@maxkatz6 Just to be clear, are the issues with integrating Serg046's EventBinder limited to compatibility with XamlX compiler (does that mean compiled bindings?), or do they apply to the design too? Because design-wise, I think EventBinding is superior to alternatives (no tying of VM to V types + passing parameters properly).

That being said, having a built-in Binding which supports methods would solve 80% of my problems, and it can later be extended with x:Bind/DXBinding-ish syntax.

Athari avatar Nov 12 '23 06:11 Athari

I would also be interested in having event handlers binding supported in Avalonia. Even if not totally MVVM friendly, it would allow more code to bemoved away from the code behind of the forms, and moved to the view model, where it could be more easily unit tested.

curia-damiano avatar Feb 21 '24 09:02 curia-damiano