-
-
Notifications
You must be signed in to change notification settings - Fork 2.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Unable to create custom binding in 11.1.0-beta1, after binding system refactor #15270
Comments
In your use case I think the Key shouldn't change during runtime but the resource can. So I'd say returning a ResourceObservable should be enough for your case. If not: there are some discussions how to create custom Localization extensions. Most of them use reflection bindings however there's at least one triyng to make it a compiled one as well. Last but not least: I also think it would be great to have a convenient solution for custom bindings or at least a documented way without using private API. |
Related: #14791 |
What did you mean by |
@timunie I've looked through my old implementation and I actually use Also, only opportunity what I have is the Avalonia/src/Avalonia.Base/Styling/Setter.cs Lines 79 to 90 in 544d537
Old approach with passing So, currently there is absolutely no way to do something with that and this is sad. |
this works for me using latest nightly (should also work in 11.1): public class MyExtenstion : MarkupExtension{
public override object ProvideValue(IServiceProvider serviceProvider)
{
var res = App.Current.GetResourceObservable("MyKey");
return res.ToBinding();
}
} it also updates the message: <Application.Resources>
<x:String x:Key="MyKey">Hello</x:String>
</Application.Resources> App.Current.Resources["MyKey"] = "You pressed me"; |
Oh, thanks actually. I've missed a |
@SKProCH you can get styled element (or resource node) to startup a lookup from using service provider. Something like dynamic resource does https://github.com/AvaloniaUI/Avalonia/blob/master/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/DynamicResourceExtension.cs#L33C17-L38 |
@SKProCH it would be easier to understand your usecase if you could file a minimal sample. Then we could probable give you an alternative or extend our API if we find it's a useful feature. |
This allows me to get style instance. For example DialogHostStyles instance, not the actual control itself. Here is the example: Avalonia/src/Avalonia.Base/Data/Core/UntypedBindingExpressionBase.cs Lines 256 to 268 in 0bf3ffc
Since now I can't create my own BindingExpression, I can't make my binding (or markup extension) work with actual control instance. For example, I can't create my analogue of DynamicResourceExtension due to that. I've make a workaround using some App theme resources for my case (i've already implemented this workaround) I've created a sample app to demonstrate this: My old implementation (with actual binding to actual control) handles this properly. This fact (what now we don't have access to actual control instance) will be also breaks other scenarios. For example, this is the quick proof of concept of binding to untyped JsonObject for DataGrid. Probably it isn't possible now. As I sad earlier its not just a "i can't hacky bind colors now", its block the possibility of creating any custom binding with user driven logic, and key point - we can't get access to actual control anymore. This 2 examples this is just what I encountered, but I'm sure what here is many others usages for custom bindings. I've looked at current bindings implementation and probably opening user access to BindingExpressionBase (and probably UntypedBindingExpressionBase) will allow creating custom binding and shouldn't(?) break anything. |
Just a bit of background on the "closing" of creating new binding types: this was done because I'm not confident enough in the current shape of the API yet to "freeze" them in their current shape. The previous API was way too open which prevented us from making improvements to the binding system throughout the lifetime of 0.10.x and I'd like to not have this problem moving forward. I've taken a bit of a look at your use-case and I think I see the problem you're trying to solve. Allowing a control library to use different resource keys based on which theme is currently in use is definitely a problem we've not solved. We need a long term-solution for issues like this, so I understand your desire for a workaround in the short-term. Having said that, I do think that the way you're doing this with a One (untested) alternative idea: could you create a custom implementation of <Setter Property="OverlayBackground" Value="{DynamicResource DialogHostOverlayBackground}" /> You would then have an e.g. Regarding your comment:
Yep, I understand. Unfortunately the openness of the API has to be balanced with API stability and the ability to make improvements without API breakages... It's a tricky balancing act. |
Thanks for extended response, @grokys.
So, is there is chance what some kind of extensibility at this point will be available when 11.1 will be out of beta stage? If yes, so, apparently this is not a big problem.
Perhaps, if you are not confident in the stability of the API, then you should allow it to be extended, for example through a separate nuget package, which will have a non-release version (e.g. Avalonia.Unstable.Bindings with note that anything can be changed at any time with breaking changes). This allows advanced users make required extensions, if they are knows what they doing. |
We already have a way of allowing access to private APIs ;) It's a little bit hidden because we don't want loads of people using it and then blaming us when we break them, but see here: https://github.com/AvaloniaUI/Avalonia/wiki/Using-private-apis-in-nuget-packages. |
Oh, interesting solution. It might not be worth using this in public packages, but it is a great feature. Thanks for feedback! In any case, the fact that blocking access to creating bindings is a temporary (in some way) problem related more to the stability of the API, and not to your reluctance, is good to hear. |
I also have created custom bindings that are now broken in Avalonia 11. I have two use cases that I don't think are supported by the built-in binding markup.
I am sure there will be other custom binding use cases that will arise and will be prevented unless an API is created. |
Describe the bug
Hello, I've noticed a "rework" of binding system internals in a 11.1 version, #13970
In my project, DialogHost.Avalonia I developed a custom Binding to pick up multiple brush values from different themes. In 10.0 it has been a quite simple - just implement an IBinding and you ready to go. With Avalonia 11 IBinding was marked with NotClientImplementable attribute, so it can't be implemented in user code. But, after a discussions in telegram the way out was been found - i can inherit from
Binding
class add explicitIBinding.Initiate
implementation to my class and create anInstancedBinding
instance there.https://github.com/AvaloniaUtils/DialogHost.Avalonia/blob/d303596bc4880cfcd07d78d381f68054851fc423/DialogHost.Avalonia/Utilities/MultiDynamicResourceExtension.cs#L27-L46
But seems like this got a rework in #13970. So, actually
IBinding.Initiate
wasn't used and left for backward compatibility. After looking through the code seems likeIBinding2.Initiate
used for now. I've tried to find a way out of this situation, but I can't. So I've open this issue as stated in warningIf you depend on this API, please open an issue with details of your use-case.
I've tried some ways to workaround this problem, but still doesn't find a proper one. Here is what I tried:
Explicit implementation of
IBinding2.Initiate
as before withIBinding.Initiate
. Can't be used since IBinding2 is internal interface.Override
Binding.Initiate
does nothing and the method marked with obsolete attribute.Avalonia/src/Markup/Avalonia.Markup/Data/Binding.cs
Lines 64 to 69 in 8117528
ProvideValue
method (since my binding is supposed to be used as markup extension anyway) seems like a perfect solution, but as I understand it evaluated during the styles initialization, nor the applying actual style (setter) to a actual control, so I can't get actual control from IServiceProvider (since actual control just doesn't exists at this point)I've looked through Avalonia's
DynamicResourceExtension
source code and this is perfect case for me, but currently its directly implementing IBinding2, and returning theDynamicResourceExpression
.DynamicResourceExpression
inherited fromUntypedBindingExpressionBase
which marked with PrivateApiAttrubite (and i can't invoke ctor), inherited fromBindingExpressionBase
which has an private protected ctor.So, my question is: how to implement my custom binding from user code?
For my use case I want to pass some set of resource keys (from different themes like Fluent, Material, etc) and want to control to pick any of them depends on what key is present in user app.
I know what it can be achieved inside of theme package, but it requires either the package will have to have a strict dependency on DialogHost, or it will be necessary to produce countless packages for almost every existing theme and force users to download them (and include them in styles) to support just a few brushes from resources.
Also, this is not just an issue about "oh my god, i can't hacky support multiple resource keys in my library", I think that's the issue about the openness and extensibility of Avalonia as this is one of the key features of this framework, and I believe that it is necessary to leave users the opportunity to create their own complex bindings, or, at least, allow some point of extensibility here.
Probably I just miss something and the solution lies on the surface? Also, pinging @grokys as the author of new binding system
To Reproduce
Try to create an binding implementation from user code.
Expected behavior
No response
Avalonia version
11.1.0-beta1
OS
No response
Additional context
No response
The text was updated successfully, but these errors were encountered: