AvaloniaUI / Avalonia

Develop Desktop, Embedded, Mobile and WebAssembly apps with C# and XAML. The most popular .NET UI client technology
https://avaloniaui.net
MIT License
25.93k stars 2.24k forks source link

Allow usage of bindings/commands for event handlers. #3766

Open grokys opened 4 years ago

grokys commented 4 years ago

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:

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

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?

lindexi commented 4 years ago

I think we should accept all.

Symbai commented 4 years ago

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?

ahopper commented 4 years ago

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.

Gillibald commented 4 years ago

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.

grokys commented 4 years ago

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:

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.

ahopper commented 4 years ago

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

Gillibald commented 4 years ago

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.

ahopper commented 4 years ago

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

kekekeks commented 4 years ago

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.

Gillibald commented 4 years ago

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.

MarcusWichelmann commented 4 years ago

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.

maxkatz6 commented 4 years ago

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:

maxkatz6 commented 4 years ago

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.

aetaylor commented 3 years ago

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

maxkatz6 commented 3 years ago

@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.

RomanSoloweow commented 3 years ago

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

ymg2006 commented 2 years ago

seems this has been done here, url.

cyraid commented 1 year ago

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 commented 1 year ago

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.

CzBuCHi commented 1 year ago

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 ...)

timunie commented 1 year ago

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

cyraid commented 1 year ago

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.

timunie commented 1 year ago

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.

Athari commented 1 year ago

@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.

timunie commented 1 year ago

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.

maxkatz6 commented 1 year ago

@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!

Athari commented 1 year ago

@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.

curia-damiano commented 8 months ago

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.